Using Mocks in Salesforce

Today I want to talk about Mocks. If you worked with other programming languages like Java or C# you may be aware of what a Mock class is, if not here is a quick explanation.

What mocking means and why should we use it?

Everything starts with Unit testing: we want to test a specific unit of code. So, in order to properly be able to test just that piece of code, you need to create all the pre-requirements that unit needs. Here is where mocking comes in handy. Mocking a class means that you programmatically establish how is going to react, what is going to do and return. As an example, think on a Case record that needs an Account because the code that we want to test does validate against the related Account record. So without Mocks you need to first insert the account to then be able to test your functionality. With Mocks, you can simulate that query that gets the related account, so that you can avoid inserting that records (avoiding validations, workflow rules and triggers as well). This makes you have more control in your test, as well as making it much faster to finish and provide accurate results.
What I will explain here is a custom Mocking framework which is mainly focused on separating the DAO layer from the rest, although it has the potential of expanding to the rest of the layers as well. We have tested this implementation in existing classes, and the duration of the test methods changed from taking close to 5 seconds (and even more) to just taking 300 milliseconds. As you can see is a HUGE win in a big implementation (in ours we are running more than 5000 test methods just for our local code).

Requirements for using Mocked classes

  • First of all we need to have the DAO class in a way so that we can mock it. This means that the DAO class needs to have an Interface with all the public methods the DAO class has.
  • Second, in the class that is consuming the DAO we need to have a static DAO instance that can be accessed from the test methods (@testVisible annotation)
  • Third, build your test method to use each of the mocked classes needed.

Let’s get into more details with an example

Let’s review this deeper by working on a real example with existing classes. For this example let’s look at ContactsListController class (ContactsListController.cls) and specifically at method getContacts():

public class ContactsListController {
    
    private String sortOrder = 'LastName';
    
    public List<Contact> getContacts() {
        
        List<Contact> results = Database.query(
            'SELECT Id, FirstName, LastName, Title, Email ' +
            'FROM Contact ' +
            'ORDER BY ' + sortOrder + ' ASC ' +
            'LIMIT 10'
        );
        return results;
    }
    
    public void sortByLastName() {
        this.sortOrder = 'LastName';
    }
    
    public void sortByFirstName() {
        this.sortOrder = 'FirstName';
    }
}

In that method we have a SOQL query that is being executed (SELECT Id, FirstName, LastName, Title, Email FROM Contact ORDER BY ‘ + sortOrder + ‘ ASC LIMIT 10). Now let’s see what the test method looks like:

@isTest
private static void testGetContacts()
{
    Contact testContact = new Contact();
    testContact.LastName = 'Doe';
    insert testContact;

    ContactsListController controller = new ContactsListController();

    Test.startTest();
    	List<Contact> contacts = controller.getContacts();
    Test.stopTest();

    System.assertEquals(1, contacts.size(), 'The method should return a contact');
}

So here for the testing purposes we need to insert a Contact in order to be able to test the method. This is obviously a simplified scenario, but think if you add new validations to the Contact object or new required fields, you will need to come and update this test (and any other test that is inserting Contact) where actually are not related to the Contact insertion. Better to actually test the method that we want and don’t have to think in all the other things.
In order to be able to do this we need to first move the SOQL query to a separate class (let’s call this new layer DAO classes) so that we can later mock it and be able to test the controller without thinking about the Contact insertion.
This is what the ContactDAO looks like:

public class ContactDAO implements IContactDAO{

    public interface IContactDAO
    {
        List<Contact> getContacts(String sortOrder);
    }

    public List<Contact> getContacts(String sortOrder) {

        return Database.query(
            'SELECT Id, FirstName, LastName, Title, Email ' +
            'FROM Contact ' +
            'ORDER BY ' + sortOrder + ' ASC ' +
            'LIMIT 10'
        );
    }
}

Note the definition of an Interface, which will give us the ability to create the mocks.
And now the we need to update the controller class to use this new DAO;

public class ContactsListController {

    private String sortOrder = 'LastName';

    @testVisible private ContactDAO.IContactDAO contactDAOInstance = new ContactDAO();

    public List<Contact> getContacts() {

        return contactDAOInstance.getContacts(sortOrder);
    }

    public void sortByLastName() {
        this.sortOrder = 'LastName';
    }

    public void sortByFirstName() {
        this.sortOrder = 'FirstName';
    }
}

Now we are ready to start mocking. We need to create then CaseDAOMock:

public class ContactDAOMock implements ContactDAO.IContactDAO {

    public List<Contact> contactsToReturn = new List<Contact>();

    public List<Contact> getContacts(String sortOrder)
    {
        return contactsToReturn;
    }
}

We need to make the class to implement the interface we created and then we implement the method and return the actual list of contacts that we want.
Now the test method will need to be changed to use this mocked class:

    @isTest
    private static void testGetContacts()
    {
        ContactDAOMock contactDAOMock = new ContactDAOMock();

        Contact testContact = new Contact();
        testContact.LastName = 'Doe';
        contactDAOMock.contactsToReturn.add(testContact);

        ContactsListController controller = new ContactsListController();
        controller.contactDAOInstance = contactDAOMock;

        Test.startTest();
        	List<Contact> contacts = controller.getContacts();
        Test.stopTest();

        System.assertEquals(1, contacts.size(), 'The method should return a contact');
    }

In here what we are doing is creating an instance of the mocked DAO and then assigning the Contact to it instead of doing the DML statement. As simple as that. Now we are truly unit testing this method.
Depending on the logic that your method has, mocking will play a key role into giving you the possibility of alter the different scenarios for your test methods.
Even with this small example, in my dev sandbox the original test method was taking 1.3 seconds and after mocking it only takes around 30 milliseconds.

Example for a formula field being used

Formula fields can’t be created “in memory”, so there is additional work you will need to do in order to be able to mock this. As an example let’s imagine we have a formula field in Contact that joins the first and last name, and is used in our code as follows:

    public boolean isJohnDoe(Contact theContact){
        if(theContact.Full_Name__c == 'Doe, John'){
            return true;
        }
        return false;
    }

And the test method to test this is:

    @isTest
    private static void testIsJohnDoe()
    {
        Contact testContact = new Contact();
        testContact.LastName = 'Doe';
        testContact.FirstName = 'John';
        insert testContact;

        testContact = [SELECT Id, Full_Name__c FROM Contact LIMIT 1];

        ContactsListController controller = new ContactsListController();

        Test.startTest();
        	boolean flag = controller.isJohnDoe(testContact);
        Test.stopTest();

        System.assertEquals(true, flag, 'The method should return true');
    }

For this simple method, we actually need to insert the Contact and then do a SOQL query to get the formula field. Again, doing a lot of stuff that is not related to the method we want to really test.
In order to be able to mock this, we actually will need to encapsulate the formula field in the DAO class, so that we can then through the Mock make it return what we need for the test.

public class ContactDAO implements IContactDAO{

    public interface IContactDAO
    {
        List<Contact> getContacts(String sortOrder);
        String getFullName(Contact theContact);
    }

    public List<Contact> getContacts(String sortOrder) {

        return Database.query(
            'SELECT Id, FirstName, LastName, Title, Email ' +
            'FROM Contact ' +
            'ORDER BY ' + sortOrder + ' ASC ' +
            'LIMIT 10'
        );
    }

    public String getFullName(Contact theContact)
    {
        return theContact.Full_Name__c;
    }
}

In the method getFullName now we have control of the formula field. Also is key to add this new method to the interface.
The mocked dao class now also needs to have this new method:

    public String getFullName(Contact theContact)
    {
        return theContact.LastName + ', ' + theContact.FirstName;
    }

The controller class has to updated to use this new method:

    public boolean isJohnDoe(Contact theContact){
        if(contactDAOInstance.getFullName(theContact) == 'Doe, John'){
            return true;
        }
        return false;
    }

The change here is that instead of getting the formula field directly, we are making the DAO class to give it to us.
Now the test method need to be changed to use the mocks:

@isTest
    private static void testIsJohnDoe()
    {
        ContactDAOMock contactDAOMock = new ContactDAOMock();

        Contact testContact = new Contact();
        testContact.LastName = 'Doe';
        testContact.FirstName = 'John';

        ContactsListController controller = new ContactsListController();
        controller.contactDAOInstance = contactDAOMock;

        Test.startTest();
        	boolean flag = controller.isJohnDoe(testContact);
        Test.stopTest();

        System.assertEquals(true, flag, 'The method should return true');
    }

Similar to what was done for the other method.
As you can see, to handle formula fields through mocks we need to do some extra work, but again, is worth it: the duration of the original test method was close to 1 second, while the one mocked didn’t go above 15 milliseconds.

Limitations

As of today this are the limitations we have, meaning that if any of this are met you will not be able to mock the classes and will need to still insert records accordingly:

  • If your unit of code uses an AggregateResult query: Still trying to work a way to mock this, but as of today this seems to be impossible in SFDC
  • If your unit of code uses CreatedDate: Salesforce provides a method to set the CreatedDate in test classes, but this actually needs the record to be inserted first (https://salesforce.stackexchange.com/questions/62/unit-testing-code-which-has-logic-around-the-createddate)
  • If you are testing a trigger you still need to insert the records (if not the trigger actually doesn’t run)
  • If your unit of code uses a formula field, you will need to create a method in the DAO class to encapsulate the formula field, allowing you to later mock it
  • Custom settings using the schema to be retrieve can’t be mocked
  • If you are testing a DAO class, you still need to do the required inserts to test the DAO results properly

Additional Resources

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s