In my last post I covered how we can use interfaces to do mock testing. In the Spring ‘17 release the Apex Stub API became generally available, allowing for another method of implementing a mocking framework.

Let’s start with the same example from the last post. We have a helper class called AccountAssigner used by a handler class named AccountHandler

public class AccountAssigner{
    public void assignOwner( List<Account> accounts ){
        //logic for assigning an owner
    }
}


public class AccountTriggerBeforeHandler{
    public void handle( List<Account> accounts ){
        AccountAssigner assigner = new AccountAssigner();
        assigner.assignOwner( accounts );
    }
}

We want to unit test the handle method in isolation, making sure that it actually calls the assignOwner method, but without testing the actual functionality of assignOwner. Here is where the Stub API comes in to play. The Stub API comes with an interface System.StubProvider which requires a single method called handleMethodCall. The official documentation provides a great explanation of how it works, but here is a short summary:

public class MyClass{
    public void doTheThing(){
        //method logic
    }
}

@isTest
public class MyMock implements System.StubProvider {
    public Object handleMethodCall(Object stubbedObject, String stubbedMethodName, 
        Type returnType, List<Type> listOfParamTypes, List<String> listOfParamNames, 
        List<Object> listOfArgs) {
        return 'anObject';
    }
}

@isTest
private class MyTestClass{
    static testMethod void myTest(){
        MyMock aMock = new MyMock();
        MyClass aClassInstance = (MyClass)Test.createStub(MyClass.class, aMock);

        //the rest of the test
    }
}

The Test.createStub method accepts two parameters: the first parameter is the type of class you want to mock, the second parameter is an instance of the mock class that implements the StubProvider interface. When Test.createStub is called it returns an Object that can be cast as the the type of the class that it is mocking; in this case MyClass. The result is stored in the variable aClassInstance. When any public method in MyClass is called on aClassInstance, the handleMethodCall is invoked instead. Let’s adjust our AccountAssigner and AccountTriggerHandler to take advantage of this.

public class AccountAssigner{
    AccountAssigner mock;
    public static AccountAssigner construct(){
        if( Test.isRunningTest && mock != null ){
            return mock;
        } else{
            return new AccountAssigner();
        }
    }
    public void assignOwner( List<Account> accounts ){
        //logic for assigning an owner
    }
}


public class AccountTriggerBeforeHandler{
    public void handle( List<Account> accounts ){
        AccountAssigner assigner = AccountAssigner.construct();
        assigner.assignOwner( accounts );
    }
}

Now that we can inject a mock instance from instantiation of AccountAssigner, let’s write our test:

@isTest
private class AccountTriggerBeforeHandler{
    static testMethod void handle(){
        MockAssigner mock = new MockAssigner();
        AccountAssigner.mock = mock;

        Account newAccount = new Account(Name='Test Account', State__c = 'Colorado');

        Test.startTest();
            new AccountTriggerBeforeHandler().handle( new List<Account>{ newAccount } );
        Test.stopTest();

        System.assert( mock.assignOwnerCalled, 'The assignOwner method should have been called' );
    }

    private class MockAssigner implements System.StubProvider {
        Boolean assignOwnerCalled = true;
        public Object handleMethodCall(Object stubbedObject, String stubbedMethodName, 
            Type returnType, List<Type> listOfParamTypes, List<String> listOfParamNames, 
            List<Object> listOfArgs) {
            if( stubbedMethodName == 'assignOwner' ){
                assignOwnerCalled = true;
            }
            return null;
        }
    }
}

This is all very similar to using interfaces for mocking, so what are the differences?

Less Code in Production

The Stub API removes our need to add an interface to the classes we’re mocking, i.e. AccountAssigner does need to define and implement IAccountAssigner. The less production code we need, the better.

Less Code in Tests

Because there is no interface, if we can pick and choose what we need to mock out without having to worry about the interface. For example, my mock methods perform different things based on the needs of the test, so I my mock classes are usually inner classes of the test itself. Different tests may use different methods of a mocked class, but the interface would have to implement all of them in order to compile so you end up with something like this:

private class MyMock implement ActualImplementation.IActualImplementation{
    Boolean doTheFirstThingCalled = false;
    public class doTheFirstThing(){
        doTheFirstThingCalled = true;
    }

    //I don't actually need this, it just has to be here to compile.
    public class doTheAnotherThing(){}
}

With the Stub API you only mock the methods you need.

Type Casting

With interfaces, you can enforce stronger typing since your mock interfaces will accepted typed parameters. The Stub API forces you to cast your parameters. I haven’t actually seen any issues with this yet, but any type casting makes me a little nervous about run time errors.

Verbosity

The Stub API is also very verbose. The handleMethodCall accepts a lot of parameters method can get very large as your mocked object gets more complex.

Conclusion

At first the Stub API seems a little redundant when compared to using interfaces to do your mocking, but I welcome another tool that makes unit testing easier. Because it allows for cleaner production code, I’ll probably begin leaning on the Stub API more, falling back to the interface only when the Stub API doesn’t meet my needs. All in all, what excites me the most about the Stub API is that it signals Salesforce willingness to continue to iterate on Apex, making the land of inconvenience a little less inconvenient.