Advanced 35 min read

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;
    }
}

A class implementing HttpCalloutMock defines exactly what response the mocked callout should return — here, a successful 200 with a fake JSON body, entirely fabricated by the test, with no real network request ever made.

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);
}

Test.setMock(HttpCalloutMock.class, new ShippingRateMock()) tells Apex: any real callout attempted during this test should be intercepted and answered by ShippingRateMock instead — ShippingService.getShippingRate() runs its actual logic unmodified, just receiving a fake response instead of a real one.

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
}

Exactly Module 31's "Testing Exceptions" lesson pattern, extended to callouts: a second mock class simulating a 500 error verifies the code's failure-handling path, not just its happy path — a scenario that would be genuinely difficult to reliably reproduce against a real external system on demand.

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());

APEX

Mocking HTTP Callouts Quiz

1. Why does Apex forbid real HTTP callouts during tests?

Log in to submit the quiz and save your score.

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.