How to fix: "Methods not defined as TestMethod do not support Web service callouts" error

Reason for the error

At some point during the execution of your failing test, your code is attempting to send an HttpRequest to a live API endpoint. Salesforce doesn't allow API requests (aka HTTP callouts) in unit tests.

Http h = new Http();
HttpRequest request = new HttpRequest();
// ...more configuration of the request object

// Attempt to make an HTTP callout...
// ...this is where the error is thrown in unit tests.
HttpResponse res = h.send(request);

The fixes

1. Use Test.setMock() to mock the callout

This has 2 steps:

  1. Create a mock which will return a fake API response. The preprogrammed response can be defined either using Static Resources, or by implementing the HttpCalloutMock interface.
  2. Use Test.setMock() to ensure the unit test uses the mock to provide the API response, rather than trying to make a real HTTP callout.

Example

The following example shows the StaticResourceCalloutMock class being used to create a mock that returns an API response defined in a Static Resource.

This is then configured to be the response-provider for the unit test using Test.setMock().

{
    "success": true,
    "fromCurrency": "GBP",
    "toCurrency": "USD",
    "date": "2022-01-02",
    "rate": 1.21047
}
GetRateAPI200Response Static Resource
@isTest
private static void GetRate_SuccessResponse_ShouldReturnRateAsDecimal() {

    // Setup
    StaticResourceCalloutMock mock = new StaticResourceCalloutMock();
    // When a callout is made...
    // ... return the API response defined by the GetRateAPI200Response
    // static resource.
    mock.setStaticResource('GetRateAPI200Response');
    mock.setStatusCode(200);
    mock.setHeader('Content-Type', 'application/json');

    // Test
    // Configure the mock to be the response-provider during the test.
    Test.setMock(HttpCalloutMock.class, mock);

    // Test that the service processes the fake API response
    // and returns a simple Decimal exchange rate.
    FXService service = new FXService();
    Decimal exchangeRate = service.getRate('USD', 'GBP');

    // Assert
    Assert.areEqual(1.21047, exchangeRate);
}
Using StaticResourceCalloutMock and Test.setMock()

2. Mock the higher-level API service method rather than the HTTP callout

Ideally, all of the low-level complexity of interacting with an API should be made more consumer-friendly by creating a reusable service method.

This opens up the possibility of mocking the service method itself, rather than the underlying HTTP callout.

Where the API service method is a dependency of the current unit under test, this is often the quicker route.

  1. Use a mocking library such as ApexMocks to create a programmable mock version of the API service.
  2. Stub (configure) the mock service to return a fake response. Note that this is not the whole API response, but just the return type of the API service method.
  3. Inject the mock API service into the consuming class so that it is used during the test instead of the real version (see the example below).

Example

The following example tests the LWCService which uses the FXService (an API service) to get exchange rate data.

The mock FXService is configured to return a Decimal value, which represents the exchange rate that would be extracted from the API response in a live callout.

@isTest
private static void GetFXRate_OnAPISuccess_ReturnSuccessResponse() {

    // Setup
    fflib_ApexMocks mocks = new fflib_ApexMocks();
    /**
     * 1. Create a mock object of the type FXService.
     */
    FXService mockFX = (FXService)mocks.mock(FXService.class);
    
    /**
     * 2. Stubbing
     * When the FXService.getRate() method is called with
     * 'GBP' and 'USD' arguments...
     * ...then return an exchange rate of 1.5.
     */
    mocks.startStubbing();
        mocks.when(mockFX.getRate('GBP', 'USD'))
            .thenReturn(1.5);
    mocks.stopStubbing();
    
    // Test
    /**
     * 3. Inject
     * Inject the mock FXService into our unit under test
     * so it is used instead of the real dependency.
     */
    LWCService.fxService = mockFX;
    LWCService.Response response = LWCService.GetFXRate('GBP', 'USD');
    
    // Assert
    System.assert(response.isSuccess);
    System.assertEquals(1.5, response.data);
}
Using ApexMocks to mock the API service method
💡
Click here for a more detailed example of mocking an API service method with ApexMocks.