Testing Integrations
By the end of this lesson, you'll be able to:
- Apply Module 31's HttpCalloutMock to test a real integration built in this module
- Test both a successful and a failed external response
- Recognize this as the closing loop this module has been building toward
Prerequisites: "Platform Events"
The service being tested
public class ShippingRateService {
public Decimal getRate(String postalCode) {
HttpRequest request = new HttpRequest();
request.setEndpoint('callout:Shipping_API/rates?postal=' + postalCode);
request.setMethod('GET');
HttpResponse response = new Http().send(request);
if (response.getStatusCode() != 200) {
throw new CalloutException('Shipping API returned status ' + response.getStatusCode());
}
Map<String, Object> parsed = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
return (Decimal) parsed.get('rate');
}
}
public class ShippingRateService {— the service class.public Decimal getRate(String postalCode) {— one focused method.HttpRequest request = new HttpRequest();— Lesson 4's request-building.request.setEndpoint('callout:Shipping_API/rates?postal=' + postalCode);— Lesson 5's Named Credential endpoint.request.setMethod('GET');— the verb.- (blank line)
HttpResponse response = new Http().send(request);— sends it.- (blank line)
if (response.getStatusCode() != 200) {— checks for failure before trusting the body.throw new CalloutException('Shipping API returned status ' + response.getStatusCode());— a real, catchable exception on failure.}— closes theif.- (blank line)
Map<String, Object> parsed = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());— Lesson 2's parsing.return (Decimal) parsed.get('rate');— the extracted value.}— closes the method.}— closes the class.
This combines nearly every lesson from this module: a Named Credential endpoint (Lesson 5), an HttpRequest/HttpResponse callout (Lesson 4), and JSON parsing (Lesson 2) — a genuinely realistic integration, ready to test.
Testing the success path
@isTest
public class ShippingRateMock implements HttpCalloutMock {
public HttpResponse respond(HttpRequest request) {
HttpResponse response = new HttpResponse();
response.setStatusCode(200);
response.setBody('{"rate": 45.50}');
return response;
}
}
@isTest
private class ShippingRateServiceTest {
@isTest
static void getRateReturnsParsedValue() {
Test.setMock(HttpCalloutMock.class, new ShippingRateMock());
Test.startTest();
Decimal rate = new ShippingRateService().getRate('8001');
Test.stopTest();
System.assertEquals(45.50, rate);
}
}
@isTest— marks the mock class.public class ShippingRateMock implements HttpCalloutMock {— Module 31's mocking interface.public HttpResponse respond(HttpRequest request) {— the one method the interface requires.HttpResponse response = new HttpResponse();— a fabricated response, no real network call involved.response.setStatusCode(200);— a successful status.response.setBody('{"rate": 45.50}');— a fabricated JSON body.return response;— hands it back to whatever code calledHttp.send().}— closes the method.}— closes the mock class.- (blank line)
@isTest— marks the test class.private class ShippingRateServiceTest {— the test class.@isTest— marks the test method.static void getRateReturnsParsedValue() {— states what's verified.Test.setMock(HttpCalloutMock.class, new ShippingRateMock());— swaps in the fabricated response for any callout made during this test.- (blank line)
Test.startTest();— the governor-limit boundary.Decimal rate = new ShippingRateService().getRate('8001');— calls the real service — itsHttp.send()returns the mock's fabricated response.Test.stopTest();— closes the boundary.- (blank line)
System.assertEquals(45.50, rate);— Module 31's specific-value assertion habit, not just "not null."}— closes the test method.}— closes the test class.
This is exactly Module 31's "Mocking HTTP Callouts" lesson, now testing genuinely real integration logic instead of an isolated example — the mock, the assertion, and the specific-value habit from Module 31 all directly applied.
Testing the failure path
@isTest
public class ShippingRateFailureMock implements HttpCalloutMock {
public HttpResponse respond(HttpRequest request) {
HttpResponse response = new HttpResponse();
response.setStatusCode(500);
response.setBody('Internal Server Error');
return response;
}
}
@isTest
static void getRateThrowsOnServerError() {
Test.setMock(HttpCalloutMock.class, new ShippingRateFailureMock());
try {
new ShippingRateService().getRate('8001');
System.assert(false, 'Expected a CalloutException');
} catch (CalloutException e) {
System.assert(e.getMessage().contains('500'));
}
}
@isTest— marks the failure mock.public class ShippingRateFailureMock implements HttpCalloutMock {— a second, separate mock for the failure scenario.public HttpResponse respond(HttpRequest request) {— the required method.HttpResponse response = new HttpResponse();— a fabricated response.response.setStatusCode(500);— this time, a failure status.response.setBody('Internal Server Error');— a plausible error body.return response;— hands it back.}— closes the method.}— closes the mock class.- (blank line)
@isTest— marks the test method.static void getRateThrowsOnServerError() {— states the exact scenario under test.Test.setMock(HttpCalloutMock.class, new ShippingRateFailureMock());— swaps in the failure mock this time.- (blank line)
try {— Module 31's exception-testing shape.new ShippingRateService().getRate('8001');— expected to throw, given the mocked500.System.assert(false, 'Expected a CalloutException');— fails the test if no exception was actually thrown.} catch (CalloutException e) {— catches the expected exception type.System.assert(e.getMessage().contains('500'));— confirms the message reflects the actual failing status code.}— closes thetry/catch.}— closes the test method.
This is Module 31's exception-testing pattern (the try/System.assert(false)/catch shape) combined with Module 31's failure-mock pattern — confirming the throw new CalloutException(...) guard in getRate actually fires correctly on a real 500 response.
Exercise
Write a mock class returning a 404 status, and a test confirming getRate throws a CalloutException containing "404" for that response.
Show hint
Follow the same pattern as the 500 mock and test in this lesson.
Testing Integrations Quiz
My Notes
Log in to keep private notes on this lesson.
Questions about this lesson
No questions yet — be the first to ask.
Log in to ask a question about this lesson.
Summary
This closing lesson connects every earlier lesson back to Module 31's HttpCalloutMock — testing a real callout, a real JSON parse, and a real error-handling path, all together.