SfSkills Domains Docs GitHub

Home / apex / apex-http-callout-mocking

apex-http-callout-mocking

HttpCalloutMock for Apex tests: HttpCalloutMock interface, StaticResourceCalloutMock, MultiStaticResourceCalloutMock, Test.setMock, multi-call mocks for pagination, error-path mocks. NOT for writing the callout itself — use apex/callouts-and-http-integrations. NOT for stubbing an Apex collaborator — use apex/apex-mocking-and-stubs.

Open the skill folder on GitHub


Apex HTTP Callout Mocking

Activate when testing Apex code that makes HTTP callouts. Apex disallows real HTTP during tests; you MUST supply a mock via Test.setMock. The default single-response mock covers simple cases, but pagination, retry, and multi-endpoint flows require stateful mocks.

Before Starting

Core Concepts

HttpCalloutMock interface

public class MyMock implements HttpCalloutMock {
    public HttpResponse respond(HttpRequest req) {
        HttpResponse r = new HttpResponse();
        r.setStatusCode(200);
        r.setBody('{"ok":true}');
        return r;
    }
}

Register via Test.setMock(HttpCalloutMock.class, new MyMock()); before the code-under-test runs.

StaticResourceCalloutMock

StaticResourceCalloutMock m = new StaticResourceCalloutMock();
m.setStaticResource('OrderResponse');  // StaticResource with JSON body
m.setStatusCode(200);
m.setHeader('Content-Type', 'application/json');
Test.setMock(HttpCalloutMock.class, m);

Stores the response body in metadata; best for large fixtures.

MultiStaticResourceCalloutMock

MultiStaticResourceCalloutMock m = new MultiStaticResourceCalloutMock();
m.setStaticResource('https://api/x/orders', 'OrdersResponse');
m.setStaticResource('https://api/x/accounts', 'AccountsResponse');
m.setStatusCode(200);
m.setHeader('Content-Type', 'application/json');
Test.setMock(HttpCalloutMock.class, m);

Routes by request endpoint; single mock handles multiple distinct endpoints.

Multi-call stateful mock (pagination)

public class PageMock implements HttpCalloutMock {
    private Integer call = 0;
    private List<String> bodies;
    public PageMock(List<String> bodies) { this.bodies = bodies; }
    public HttpResponse respond(HttpRequest req) {
        HttpResponse r = new HttpResponse();
        r.setStatusCode(200);
        r.setBody(call < bodies.size() ? bodies[call++] : '{}');
        return r;
    }
}

Required when a single endpoint is called multiple times and each response differs (pagination, retry).

Uncommitted-work-pending error

If DML precedes a callout in the same transaction, Salesforce throws CalloutException: You have uncommitted work pending. Cannot be "fixed" in a mock — refactor to Queueable or call DML AFTER all callouts complete.

Common Patterns

Pattern: Error-path test

public class ErrorMock implements HttpCalloutMock {
    public HttpResponse respond(HttpRequest req) {
        HttpResponse r = new HttpResponse();
        r.setStatusCode(500);
        r.setBody('{"error":"down"}');
        return r;
    }
}

@IsTest static void testHandlesServerError() {
    Test.setMock(HttpCalloutMock.class, new ErrorMock());
    Test.startTest();
    Boolean result = OrderService.fetch();
    Test.stopTest();
    System.assertEquals(false, result);
}

Pattern: Endpoint-dispatched mock

public class Router implements HttpCalloutMock {
    public HttpResponse respond(HttpRequest req) {
        HttpResponse r = new HttpResponse();
        r.setStatusCode(200);
        if (req.getEndpoint().contains('/orders')) r.setBody(ORDERS_JSON);
        else if (req.getEndpoint().contains('/auth')) r.setBody(AUTH_JSON);
        else r.setStatusCode(404);
        return r;
    }
}

Pattern: Header assertion

public class HeaderMock implements HttpCalloutMock {
    public static String capturedAuth;
    public HttpResponse respond(HttpRequest req) {
        capturedAuth = req.getHeader('Authorization');
        // ... return response
    }
}
// In test: assert HeaderMock.capturedAuth == 'Bearer expected_token'

Decision Guidance

SituationMock
One endpoint, one responseStaticResourceCalloutMock
Many endpoints, one eachMultiStaticResourceCalloutMock
One endpoint, multiple calls returning different bodiesCustom HttpCalloutMock with call counter
Error-path testingCustom mock returning non-2xx
Assert on request headers/bodyCustom mock capturing into static fields
  1. Identify every callout the test exercises (endpoint, verb, count).
  2. Store large JSON fixtures as StaticResource files.
  3. Choose mock type per the decision table.
  4. For multi-call: implement counter-based HttpCalloutMock.
  5. Register via Test.setMock BEFORE the code-under-test is invoked.
  6. For error paths, add a parallel test with an error-returning mock.
  7. Assert on side-effects AND, where relevant, on captured request fields.

Review Checklist

Salesforce-Specific Gotchas

  1. Test.setMock must be called before any callout in the transaction. Calling it after the first callout is ignored.
  2. You cannot perform a real callout in a test even with no setMock — it throws. Forgetting to set a mock yields a test failure, not a skip.
  3. HttpCalloutMock respond method runs within the test's governor context — don't do heavy DML inside it.
  4. MultiStaticResourceCalloutMock matches the endpoint string literally — query parameters in the request but not the mock registration cause misses.

Output Artifacts

ArtifactDescription
Custom HttpCalloutMockStateful mock for multi-call tests
StaticResource JSON fixturesReviewable response bodies
Error-path mocks4xx/5xx responses
Header-capture mockAssertion helper for request validation

Package files