Mocking HTTP Callouts
By the end of this lesson, you'll be able to:
- Explain why a real HTTP callout can't run inside a test
- Implement HttpCalloutMock to simulate an external response
- Test both a successful and a failed callout response
Prerequisites: "Test Data Factories"
Why real callouts can't run in a test
Apex tests explicitly cannot make real HTTP callouts — attempting one throws a CalloutException. This is deliberate: tests need to run reliably and repeatably regardless of whether an external system is actually available, fast, or even still exists months later, and they should never have side effects on a real third-party service (a later Integration module covers callouts fully; this lesson focuses purely on testing them).
Implementing a mock response
@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— marks the class as test-only.public class ShippingRateMock implements HttpCalloutMock {— a class implementingHttpCalloutMockdefines exactly what response the mocked callout should return.public HttpResponse respond(HttpRequest request) {— the one method the interface requires.HttpResponse response = new HttpResponse();— builds a fake response.response.setStatusCode(200);— a successful status.response.setBody('{"rate": 45.50}');— a fake JSON body, entirely fabricated by the test.return response;— hands it back, with no real network request ever made.}— closes the method.}— closes the class.
Using the mock in a test
@isTest
static void getShippingRateReturnsParsedRate() {
Test.setMock(HttpCalloutMock.class, new ShippingRateMock());
Test.startTest();
Decimal rate = ShippingService.getShippingRate('12345');
Test.stopTest();
System.assertEquals(45.50, rate);
}
@isTest— marks the test method.static void getShippingRateReturnsParsedRate() {— the test.Test.setMock(HttpCalloutMock.class, new ShippingRateMock());— tells Apex: any real callout attempted during this test should be intercepted and answered byShippingRateMockinstead.- (blank line)
Test.startTest();— marks the boundary.Decimal rate = ShippingService.getShippingRate('12345');— runs its actual logic unmodified, just receiving a fake response instead of a real one.Test.stopTest();— marks the end.- (blank line)
System.assertEquals(45.50, rate);— confirms the response was parsed correctly.}— closes the test method.
Testing a failure response too
@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 getShippingRateHandlesServerError() {
Test.setMock(HttpCalloutMock.class, new ShippingRateFailureMock());
Test.startTest();
Decimal rate = ShippingService.getShippingRate('12345');
Test.stopTest();
System.assertEquals(null, rate); // however the service is designed to handle a failure
}
@isTest— marks the class.public class ShippingRateFailureMock implements HttpCalloutMock {— exactly Module 31's "Testing Exceptions" lesson pattern, extended to callouts — a second mock class.public HttpResponse respond(HttpRequest request) {— the required method.HttpResponse response = new HttpResponse();— builds a fake response.response.setStatusCode(500);— this time, simulating a server error.response.setBody('Internal Server Error');— a fake error body.return response;— hands it back.}— closes the method.}— closes the class.- (blank line)
@isTest— marks the test method.static void getShippingRateHandlesServerError() {— verifies the code's failure-handling path, not just its happy path.Test.setMock(HttpCalloutMock.class, new ShippingRateFailureMock());— swaps in the failure mock.- (blank line)
Test.startTest();— marks the boundary.Decimal rate = ShippingService.getShippingRate('12345');— runs against the simulated failure.Test.stopTest();— marks the end.- (blank line)
System.assertEquals(null, rate);— however the service is designed to handle a failure — a scenario that would be genuinely difficult to reliably reproduce against a real external system on demand.}— closes the test method.
Exercise
Write a mock class OrderStatusMock returning a 200 response with the body '{"status": "shipped"}', then a test method using it.
Show hint
implements HttpCalloutMock, then Test.setMock(HttpCalloutMock.class, new OrderStatusMock());
Mocking HTTP Callouts 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
Apex forbids real HTTP callouts during tests — HttpCalloutMock lets a test simulate exactly what an external system would have returned, without ever actually calling it.