Getting Started
HTTP Mock Lib mocks HTTP callouts in Apex tests with one class and a fluent API: say what each endpoint returns, run your code, assert on what it sent.
Installation
See the Installation Guide.
Your First Mock
The code under test — a service that registers a shipment with a courier API and returns the tracking number:
public with sharing class ShipmentService {
public String registerShipment(String orderNumber) {
HttpRequest request = new HttpRequest();
request.setEndpoint('callout:Courier_API/api/v2/shipments');
request.setMethod('POST');
request.setHeader('Content-Type', 'application/json');
request.setBody(JSON.serialize(new Map<String, String>{ 'orderNumber' => orderNumber }));
HttpResponse response = new Http().send(request);
Map<String, Object> payload = (Map<String, Object>) JSON.deserializeUntyped(response.getBody());
return (String) payload.get('trackingNumber');
}
}The test mocks the endpoint, runs the service, and asserts on the result:
@IsTest
private class ShipmentServiceTest {
@IsTest
static void registerShipmentReturnsTrackingNumber() {
new HttpMock()
.whenPostOn('/api/v2/shipments')
.body('{ "trackingNumber": "1Z999AA10123456784" }')
.statusCodeCreated()
.mock();
Test.startTest();
String trackingNumber = new ShipmentService().registerShipment('ORD-1042');
Test.stopTest();
Assert.areEqual('1Z999AA10123456784', trackingNumber, 'Tracking number should come from the courier response');
}
}The mocked path matches any request endpoint that contains it — so /api/v2/shipments also matches callout:Courier_API/api/v2/shipments and full URLs with query parameters. See Endpoint Matching.
Defaults
Every mocked endpoint starts with status 200, content type application/json, and body {}. Set only what your test cares about:
new HttpMock()
.whenGetOn('/api/v2/shipments/1Z999AA10123456784')
.mock(); // responds 200 with {}Common Patterns
More Than One Endpoint
Each when[Method]On starts a new stub — mock the whole integration in one chain:
new HttpMock()
.whenPostOn('/oauth/token')
.body('{ "access_token": "a1b2c3" }')
.whenPostOn('/api/v2/shipments')
.body('{ "trackingNumber": "1Z999AA10123456784" }')
.statusCodeCreated()
.mock();Object Bodies
Anything that isn't a String or Blob is JSON-serialized:
.body(new Map<String, String>{ 'trackingNumber' => '1Z999AA10123456784' })Error Responses
Mock the failure your integration has error handling for, and assert on that handling:
new HttpMock()
.whenPostOn('/api/v2/shipments')
.body('{ "error": "address_not_routable" }')
.statusCodeBadRequest()
.mock();
Test.startTest();
ShipmentResult result = new ShipmentService().registerShipmentSafe('ORD-1042');
Test.stopTest();
Assert.isFalse(result.success, 'An unroutable address should not register a shipment');
Assert.areEqual('address_not_routable', result.errorCode, 'Error code should come from the courier');Retries
Stub the same endpoint several times and the responses are consumed in order — the last one repeats. An exception can take a turn in that queue:
new HttpMock()
.whenGetOn('/api/v2/shipments').throwsException(new CalloutException('Read timed out'))
.whenGetOn('/api/v2/shipments').statusCodeOk()
.mock();
// first callout throws, the retry succeedsSee Exceptions.
Asserting On What Was Sent
The mock records every request. Read them back to verify the payload your code built:
Assert.areEqual(1, HttpMock.requestsTo('/api/v2/shipments').post(), 'One shipment should be registered');
Assert.areEqual('{"orderNumber":"ORD-1042"}', HttpMock.requestsTo('/api/v2/shipments').last().getBody(), 'Payload should carry the order number');See Requests.
