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.
Scanned 9/6/2026
Install to Claude Code
npx -y skills add PranavNagrecha/AwesomeSalesforceSkills --skill apex-http-callout-mocking --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Apex Http Callout Mocking?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/pranavnagrecha-apex-http-callout-mocking)More formats (shields.io, HTML) on the badges page.
---
name: apex-http-callout-mocking
description: "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."
category: apex
salesforce-version: "Spring '25+"
well-architected-pillars:
- Reliability
tags:
- apex
- testing
- http-callout
- mocks
- test-setmock
triggers:
- "httpcalloutmock apex test mock http callout"
- "test.setmock multiple callouts pagination"
- "staticresourcecalloutmock vs httpcalloutmock which"
- "multistaticresourcecalloutmock endpoint routing test"
- "mock http response error 500 apex retry test"
- "you have uncommitted work pending callout test"
inputs:
- Callout shape (endpoints, verbs, headers)
- Number of calls per test
- Error scenarios to exercise
outputs:
- HttpCalloutMock implementation
- Test method with Test.setMock
- Multi-call routing logic
dependencies: []
version: 1.0.0
author: Pranav Nagrecha
updated: 2026-04-22
status: stub
---
# 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
- **Enumerate callouts.** How many requests, which endpoints, which status-code paths?
- **Pick the mock shape.** Single response → `StaticResourceCalloutMock`. Multiple responses → custom `HttpCalloutMock`. Per-endpoint routing → `MultiStaticResourceCalloutMock`.
- **Load-bearing response body content.** Store JSON in a `StaticResource` instead of inline strings to keep tests reviewable.
## 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
| Situation | Mock |
|---|---|
| One endpoint, one response | `StaticResourceCalloutMock` |
| Many endpoints, one each | `MultiStaticResourceCalloutMock` |
| One endpoint, multiple calls returning different bodies | Custom `HttpCalloutMock` with call counter |
| Error-path testing | Custom mock returning non-2xx |
| Assert on request headers/body | Custom mock capturing into static fields |
## Recommended Workflow
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
- [ ] `Test.setMock` called before the code-under-test runs
- [ ] Multi-call scenarios use a stateful custom mock (not a single-response mock reused)
- [ ] Error-path test present (non-2xx status)
- [ ] Large fixtures in `StaticResource`, not inline
- [ ] Test does no DML between callouts that triggers "uncommitted work pending"
- [ ] Request-level assertions (headers/body) where security/auth matters
## 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
| Artifact | Description |
|---|---|
| Custom `HttpCalloutMock` | Stateful mock for multi-call tests |
| `StaticResource` JSON fixtures | Reviewable response bodies |
| Error-path mocks | 4xx/5xx responses |
| Header-capture mock | Assertion helper for request validation |
## Related Skills
- `apex/apex-test-setup-patterns` — test structure + startTest/stopTest
- `apex/callouts-and-http-integrations` — the callout code itself
- `integration/rest-api-pagination-patterns` — pagination tests need multi-call mocks
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!