Skip to content

Creating Mocks

David Frudd edited this page Nov 26, 2017 · 1 revision

Note: This tutorial contains examples from the Apex Mocks SFDX Sample App. Check it out to see all the code.

Unsurprisingly, ApexMocks is all about mocks. Let's get our vocabulary clear so we're all talking the same language!

Great code is divided into small, single-purpose, reusable, modular classes. Each class does one thing, only one thing, and it does that thing well. If I was a class, I would be able to do some processing, or I could instead orchestrate other classes to do processing for me. Those classes can be referred to as my dependencies.

When we unit test each class, we want to isolate its dependencies. We do this by replacing the real dependencies with mocks. A mock is a "Test Double" - a lean object which emulates the one it is standing in for. It has no logic of its own, just the behaviour we define within the context of our unit test.

This means we can build more expressive tests. The name of the test method will tell us the required behavior from the unit under test. And we'll explicitly outline the desired behavior in the dependencies (which are not under test).

So how do we create a mock? There are currently two ways. In both cases, we need an instance of fflib_ApexMocks(), which acts as our Stub Provider.

1. Creating Mocks with fflib_ApexMocks.mock(..) (Internally Uses the Stub API)

This is the recommended approach. As an example, let's create a mock of AccountsSelector.

fflib_ApexMocks mocks = new fflib_ApexMocks();
AccountsSelector mockSelector = (AccountsSelector)mocks.mock(AccountsSelector.class);

We have created a mock: mockSelector. We can stub the mock to behave in a specific way. We can verify how the mock was used during the test, if at all. Notice how we needed to explicitly cast it to an AccountsSelector: (AccountsSelector)mocks.mock(...)?

What is the mockSelector? If you dump it out in debug logs, you'll find it's actually a AccountsSelector__sfdc_ApexStub class. You won't find this class in source control anywhere! This class was dynamically created at runtime, and it extends AccountsSelector.

So it inherits all of the AccountsSelector methods, and as far as the code is concerned, the mockSelector is just a plain old AccountsSelector. But this ain't no ordinary selector - any time you invoke a method on this mock, it will be handed straight over to the mock framework to register the invocation and invoke your predefined behavior.

Before you get carried away, the Stub API has limitations:

  • It only works in tests.
    • So you can't use this trick to instantiate dynamic subclasses in production code.
  • It only works in your namespace.
    • So you can't mock out classes from other namespaces.
    • On the other hand, mocking best practices say you should only mock code that you own anyway!
  • There are limitations around mockable types.
    • The full list is here.
    • It includes private classes, inner classes, abstract classes, classes that implement the Batchable interface.

2. Creating Mocks with The ApexMocks Generator

Before the Stub API, the ApexMocks generator is a Java application that parses your Apex classes to generate mock classes. Where possible, you should create mocks using fflib_ApexMocks.mock(..). If for some reason you can't (e.g. you need to mock a Batchable class), then you can still use the ApexMocks generator.

We'll use AccountsSelector as an example again. AccountsSelector cannot be extended (it doesn't have the virtual keyword), so we're going to define a new interface IAccountsSelector, and let the generator create a test double that implements the interface.

  1. Define IAccountsSelector
    public interface IAccountsSelector
    {
    	Account[] getForNames(Set<String> names);
    }
  2. Change AccountsSelector to implement IAccountsSelector.
    Change the AccountsSelector.newInstance method return type to IAccountsSelector.
    public class AccountsSelector implements IAccountsSelector
    {
    	@TestVisible
    	private static IAccountsSelector instance = null;
    
    	public static IAccountsSelector newInstance()
    	{
    		if (instance == null)
    		{
    			instance = new AccountsSelector();
    		}
    
    		return instance;
    	}
    	//...
  3. Create an interfacemocks.properties file that tells the generator the name of the generated class (and optionally if you want to modify the keywords or extend other classes).
    IAccountsSelector=AccountsSelector
    
  4. Generate the mock IAccountsSelector, by running this command:
    java -jar apex-mocks-generator-4.0.1.jar "force/main/default/classes" "interfacemocks.properties" "Mocks" "force/main/default/classes" "41.0"
    • Now you should have a new class, force/main/default/classes/Mocks.cls, containing an AccountsSelector inner class that you can use as a test double.
    /* Generated by apex-mocks-generator version 4.0.1 */
    @isTest
    public class Mocks
    {
    	public class AccountsSelector implements IAccountsSelector
    	{
    		private fflib_ApexMocks mocks;
    
    		public AccountsSelector(fflib_ApexMocks mocks)
    		{
    			this.mocks = mocks;
    		}
    
    		public Account[] getForNames(Set<String> names)
    		{
    			return (Account[]) mocks.mockNonVoidMethod(this, 'getForNames', new List<Type> {System.Type.forName('Set<String>')}, new List<Object> {names});
    		}
    	}
    
    }
  5. And finally, you can instantiate this in your test like this:
    fflib_ApexMocks mocks = new fflib_ApexMocks();
    IAccountsSelector mockSelector = new Mocks.AccountsSelector(mocks);

You can now commit the Mocks file into source control, but if any of the interfaces inside are changed, you would need to regenerate it. Alternatively, you could gitignore the Mocks file, and create a build process that regenerates it before deployment.