Salesforce Mock in Apex Tests

Testing is often an overlooked aspect of Salesforce development. We focus on our business logic, ensuring our changes impact the system correctly and satisfy all requirements. Then, deployment day comes, and guess what? We forgot to write proper Apex tests — …again.

And the scenario looks something like this:
Thanks to AI, we can use Dev Assistant (or other tool) to generate boilerplate tests. Great, we have something! Let’s quickly run them… and then tests are running for minutes, or even hours. We start to question ourselves: “Why are these tests taking so long! It’s already 5 PM, and I want to close my laptop ASAP”.

This is why you should rethink how you write your tests. Normally, we create test data, insert it into the database, mock HTTP callouts, and run our assertions. But what if I told you that you don’t always need to take this slow and expensive approach?

There’s a quicker and better way to structure your tests — with mocking and stubbing.


Mocking in Apex

You’re probably already familiar with mocking, especially when working with HTTP callouts. But it doesn’t have to stop there — you can mock all sorts of things, making your tests faster, more predictable, and less dependent on inserting actual data in the system.

What is Mocking?

Mocking is the practice of replacing real objects with fake implementations that simulate the behavior of the real ones. Instead of executing real logic (which might involve slow DML operations or external integrations), mock provides predefined responses.

Mocking HTTP Callouts

When testing, we can’t use callouts, so Salesforce makes us use Http Mock instead. And that’s good, service endpoints, especially test ones, are usually unstable and unpredictable. It is better to use mock, with expected and reusable results. Here is a simple example of HttpCalloutMock interface implementation in unit tests.

@IsTest
global class ExternalServiceCalloutMock implements HttpCalloutMock {
    private Integer statusCode = 200;
    private String body = '{"success": true}';

    global ExternalServiceCalloutMock(Integer statusCode, String body) {
        this.statusCode = statusCode;
        this.body = body;
    }

    global HTTPResponse respond(HTTPRequest request) {
        HttpResponse response = new HttpResponse();

        response.setStatusCode(this.statusCode);
        response.setBody(this.body);

        return response;
    }
}

@IsTest
private class ExternalServiceCalloutTest {
    @IsTest
    static void testHttpCallout() {
        Test.setMock(HttpCalloutMock.class, new ExternalServiceCalloutMock(400, 'null'));

        Test.startTest();
        HttpResponse result = ExternalServiceCalloutMock.getData();
        Test.stopTest();

        Assert.areEqual('null', result.getBody());
    }
}

Can we make it easier?

Yes, we can! Check our HTTP Mock Lib.

Using it, the above example would look like this (all in one class!):

@IsTest
private class ExternalServiceCalloutTest {
    @IsTest
    static void testHttpCallout() {
        new HttpMock()
            .get('/external/service').body('null').statusCodeBadRequest()
            .mock();

        Test.startTest();
        HttpResponse result = ExternalServiceCalloutMock.getData();
        Test.stopTest();

        Assert.areEqual('null', result.getBody());
    }
}

Mocking HTTP Callouts but not in tests?

Are you working with Scratch Orgs? Do you wish to test something, but you have to set 132 external connections? No more!

How to do it:

public class MulesoftConnector {
    public HttpResponse getAccounts() {
        HttpRequest request = RequestBuilder.getMulesoftRequest('Accounts'); // we assume there is something that will prepare a request

        Boolean runningInSandbox = [SELECT IsSandbox FROM Organization LIMIT 1].IsSandbox;
        if(runningInSandbox) { // check depends on your strategy
            return MulesoftMocks.accounts();
        }

        return new Http().send(request);
    }
}

public class MulesoftMocks {
    public static String accounts() {
        return JSON.serialize(new Map<String, String>{ 'Id' => new Account(Name = 'Test Account')});
    }
}

That way, even on your scratch org, you will get some data to work with.

Just a small warning!
Make sure you know what you are doing! In this example, I’m checking if we are in the sandbox, this means on your UAT, QA, other non production instance, code will return Mock data even if there is test integration in place and in working order. Make your own judgment when this should take place. For example, you can use custom metadata to build a more sophisticated solution.

Mocking SOQL Queries

This might not be so obvious, but we can mock SOQL queries and return predefined data. This is especially useful in very large instances, with a lot of database operations. It’s not only saves time but also provides a more stable and controllable environment to run tests.

Simple example to return data during a test:

public class ContactsController {

    @AuraEnabled
    public static List<Contact> getAccountContacts(Id accountId) {

        if(Test.isRunningTest()) {
            return MockProvider.accountContacts;
        }

        return [SELECT Id, Name FROM Contact WHERE AccountId = :accountId];
    }
}

public class MockProvider {
    public static List<Contact> getContacts(Id accountId) {
        return new List<Contact> {
            new Contact(Id = 'MockProvider1', Name = 'Bob', AccountId = accountId),
            new Contact(Id = 'MockProvider2', Name = 'James', AccountId = accountId)
        };
    }
}

Make sure to add all fields in the mock, so that the tests we are performing are as close as possible to the real scenario.

Ready to use SOQL mocking

If you are looking for a ready to go solution for SOQL, check SOQL Lib with built-in mocking!

Using SOQL Lib above example would look something like this:

public class ContactsController {

    @AuraEnabled
    public static List<Contact> getAccountContacts(Id accountId) {
        return SOQL_Contact.byAccountId(accountId).mockId('ContactsController').toList();
    }
}

@IsTest
private class ContactsControllerTest {

    @IsTest
    private static void getAccountContacts() {
        SOQL.setMock('ContactsController', new List<Contact> {
            new Contact(Id = 'MockProvider1', Name = 'Bob', AccountId = accountId),
            new Contact(Id = 'MockProvider2', Name = 'James', AccountId = accountId)
        });

        Test.startTest();
        List<Contact> contacts = ContactsController.getAccountContacts();
        Test.stopTest();

        Assert.areEqual(2, contacts.size(), 'Controller should return 2 contacts!');
    }
}

Stubbing in Apex

Stubbing is a more advanced technique than mocking, but it’s built into the Salesforce ecosystem via the Stub API.

What is Stubbing?

Stubbing is a way to create fake implementations of interfaces or virtual classes. Unlike simple mocks, which only return predefined values, stubs can have logic inside them to simulate different behaviors dynamically.

This approach is valuable because it isolates parts of your code, making unit tests more reliable and reducing dependencies between classes. Instead of testing a chain of interdependent logic, you can focus on just one piece at a time.

Stub Implementation

@IsTest
public class StubProvider implements System.StubProvider {
    public Object handleMethodCall(Object stubbedObject, String methodName, Type returnType, List<Type> paramTypes, List<Object> paramValues) {
        if (methodName == 'getActiveAccounts') {
            List<Account> activeAccounts = new List<Account>();

            for(Account value: (List<Account>) paramValues){
                if(value.IsActive__c) {
                    activeAccounts.add(value);
                }
            }

            return activeAccounts;
        }

        return null;
    }
}

Using the Stub in a Test

@IsTest
private class MyServiceTest {
    @IsTest
    static void testWithStubbedService() {
        AccountController controller = (AccountController) Test.createStub(AccountController.class, new StubProvider());

        Test.startTest();
        List<Account> activeAccounts = controller.getActiveAccounts([SELECT Id, IsActive__c FROM Account]);
        Test.stopTest();

        Assert.isFalse(activeAccounts.isEmpty(), 'At least one account should be returned!');
    }
}

Conclusion – Is it worth?

Mocking or stubbing will provide some benefits during your tests:
Reduce database operations in tests, making them faster.
Isolate logic, so tests are less dependent on real data.
Make unit tests more predictable by removing reliance on external factors.
Test data is not dependent on automations so that tests are not impacted by changes in the Triggers or Flows.

But also you need to remember that:
Mocks don’t insert data and the developer is responsible for data quality.

When to use what?

  • Use mocking when you need a simple replacement for an object, typically returning static or predefined values.
  • Use stubbing when you require dynamic behavior in your test, excluding execution of some parts of the code base.

And remember: writing Apex tests isn’t just about reaching 75% code coverage — it’s about ensuring that the system we build is fast and reliable.

Next time you write a test, think before you insert — maybe you don’t need actual records in the database at all. Instead, try mocking or stubbing to supercharge your Apex tests! 🚀

Jan Śledziewski
Jan Śledziewski
Senior Salesforce Developer
Full-stack Salesforce Developer and Technical Product Owner. On Salesforce trail since 2018. Focused on code quality and solution architecture.