How to write Unit Tests with ServiceLocator


The service locator pattern is a design pattern used in software development to encapsulate the processes involved in obtaining a service with a strong abstraction layer.

We already mention it in our article: How to develop a flexible Apex code for Salesforce.

This pattern uses a central registry known as the “service locator”, which on request returns the information necessary to perform a certain task. The approach simplifies component-based applications where all dependencies are listed at the beginning of the whole application design, consequently making traditional dependency injection a more complex way of connecting objects.

For example, you need to write a unit test for RequestSender class that performs a callout to web service.

public class RequestSender {

    public String getGooglePage() {
        HttpRequest request = new HttpRequest();
        request.setMethod('GET');
        request.setEndpoint('https://www.google.com/');
        Http sender = new Http();
        HttpResponse response = sender.send(request);
        if (response.getStatusCode() == 200) {
            return response.getBody();
        }
        return null;
    }
}

First of all that you need to do is implement getInstance method to create an instance of the class through ServiceLocator to give ServiceLocator the ability to put RequestSender to the registry.

Then you need to refactor a current method and extract a new one method to send Http Request (sendRequest) and make it and the whole class virtual to give the ability to change the behavior of the method.

public virtual class RequestSender {

    public static RequestSender getInstance() {
        return (RequestSender)
            ServiceLocator.getInstance(RequestSender.class);
    }

    public virtual HttpResponse sendRequest(HttpRequest request) {
        Http sender = new Http();
        return sender.send(request);
    }

    public String getGooglePage() {
        HttpRequest request = new HttpRequest();
        request.setMethod('GET');
        request.setEndpoint('https://www.google.com/');
        HttpResponse response = sendRequest(request);
        if (response.getStatusCode() == 200) {
            return response.getBody();
        }
        return null;
    }
}

To cover the class by unit tests you need to implement FakeRequestSender that will extend RequestSender and override the behavior of sendRequest the method, because we can’t perform callouts in the unit tests.

@isTest
public class RequestSenderTest {

    @isTest
    static void getGooglePageWithServiceLocatorTest() {
        ServiceLocator.setMock(RequestSender.class, 
            FakeRequestSender.class);
        String body = RequestSender.getInstance().getGooglePage();
        System.assertEquals('GOOGLE', body);
    }

    public class FakeRequestSender extends RequestSender {
        public override HttpResponse sendRequest(HttpRequest request) {
            HttpResponse response = new HttpResponse();
            response.setStatusCode(200);
            response.setBody('GOOGLE');
            return response;
        }
    }
}

Then you need to set it in ServiceLocator using ServiceLocator.setMock (RequestSender.class, FakeRequestSender.class);

ServiceLocator will give you an ability to override any behavior in any method in the class that helps you to write unit tests quickly and efficiently.

If you have any questions or problems related to Salesforce Development – let us know. We always glad to help you.