The Joys of Dependency Injection Part 3 - Apex Stub API
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.
The Joys of Dependency Injection Part 2 - Making a Mockery
In my last post I discussed interfaces and how they can be used for dependency injection in Salesforce, allowing for flexible implementations of features. However, I can probably count on my hand the number of times I’ve actually had to use interfaces for this purpose. Designing to an interface assumes you are able to predict if and how something will be reused or repurposed. Instead, interfaces are particularly useful for mocking functionality during unit tests.
Most of the testing I see is integration testing, also known as black box testing. Trigger tests usually fall into this category: when I insert object X, I expect Y to happen. For example:
trigger AccountTrigger on Account (before insert) {
AccountTriggerBeforeInsertHandler.handle(Trigger.new);
}
public class AccountTriggerBeforeHandler{
public void handle( List<Account> accounts ){
AccountAssigner assigner = new AccountAssigner();
assigner.assignOwner( accounts );
}
}
@isTest
private class AccountTriggerTest {
@isTest private void beforeInsert(){
User expectedAssignee = TestUtils.buildAssignedUserForState('Colorado'); //Some test fixture method that creates our expected user;
Account newAccount = new Account(Name='Test Account', State__c = 'Colorado');
Test.startTest();
insert newAccount;
Test.stopTest();
newAccount = [SELECT OwnerId FROM Account WHERE Id = :newAccount.Id];
System.assertEquals( expectedAssignee.Id, newAccount.OwnerId, 'The Owner should be set correctly' );
}
}
The test above tests that the before insert handler sets the Owner of an Account is set correctly. You could test other scenarios, but the minimum integration test here is that when you insert an Account, the Account gets assigned to someone.
Unit tests, on the other hand, are a little more specific. What constitutes a unit is up for debate, but I consider it a single public method. If it is composed of several private methods, I still consider it a single unit. In our example, the AccountAssigner
class would have its own set of tests that test all the scenarios for the assignOwner
method.
But what about the handle
method in AccountTriggerBeforeHandler
? The integration test that fires the trigger will give us the coverage we need, but shouldn’t we test the handler itself? And what would that test even look like? Unit tests should test what the method does. At first glance it seems that handle
assigns owners to Accounts records. However, the method actually just calls the assignOwner
method, it doesn’t actually do the assigning. So the test should make sure that our method does just that. For example:
@isTest
private class AccountTriggerBeforeHandlerTest {
@isTest private void handle(){
User expectedAssignee = TestUtils.buildAssignedUserForState('Colorado'); //Some test fixture method that creates our expected user;
Account newAccount = new Account(Name='Test Account', State__c = 'Colorado');
Test.startTest();
new AccountTriggerBeforeHandler().handle(new List<Account>{ newAccount } );
Test.stopTest();
System.assertEquals( expectedAssignee.Id, newAccount.OwnerId, 'The Owner should be set correctly' );
}
}
This test is so similar to the integration test that we might as well not have it all. But how else can we test it? That’s where interfaces come. We’ll create an interface for the AccountAssigner object, with a special method to replace the constructor.
public class AccountAssigner implements IAccountAssigner{
public interface IAccountAssigner{
void assignOwner( List<Account> accounts );
}
@TestVisible static IAccountAssigner mock;
public static construct(){
if( Test.isRunningTest() && mock != NULL ){
return mock;
} else{
return new AccountAssigner();
}
}
public void assignOwner( List<Account> accounts ){
//logic for assigning an owner
}
}
I usually keep the interface within the class; it reduces the number of files you have and it’s only purpose is to mock this class. However, feel free to put it in another file you want. I also added a construct
method that returns a mock if we want and only if it’s a test. Now that we have an interface, instead of instantiating AccountAssigner
in the handle
method, we ‘ll use the new construct
method, allowing us to inject our mock during tests:
public class AccountTriggerBeforeHandler{
public void handle( List<Account> accounts ){
AccountAssigner.IAccountAssigner assigner = AccountAssigner.construct();
assigner.assignOwner( accounts );
}
}
We’re now set up our to mock AccountAssigner
in our test:
@isTest
private class AccountTriggerBeforeHandlerTest {
public class MockAssigner implements AccountAssigner.IAccountAssigner{
Boolean assignOwnerCalled = false;
public void assignOwner(List<Account> accounts){
assignOwnerCalled = true;
}
}
@isTest private 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' );
}
}
First we create MockAssigner
, a class that implements the IAccountAssigner
interface. All the mock will do is mark that the method has been called, which is all that we care about in this test. The test method will then instantiate a mock and inject it into the mock
variable in AccountAssigner
, which will then in turn be used by the construct
method.
We now have an adequate unit test for our handle
method; we programatically know that it calls assignOwner
. We also decouple the functionality of AccountTriggerBeforeHandler
from AccountAssigner
. Should we need to change the logic within assignOwner
, the unit test for handle
would not have to change. We could also test what happens if assignOwner
threw an exception, hit governor limits, etc. because the test has complete control over what happens in the method. This gives us a lot of flexibility to write very comprehensive tests. As a side effect, programming this way also forces you to build in a more modular fashion.
I’ve been doing this for a few years now and it works nicely, but it also means a lot of extra code in production: interfaces, new contstructors, etc. In the Winter ‘17 release the Apex Stub API was announced. In the final part of this series, we’ll discuss how to use the Stub API and how it compares to using interfaces for mocking.
The Joys of Dependency Injection Part 1 - Interfaces
Dependency injection helps you make code that is easy to maintain and test by
- Allowing “units” of code to only be concerned with their own functionality
- Making dependencies easily swappable without altering dependent code
- Allowing you to mock/control dependencies during tests so that you isolate what you are testing
In Salesforce, this is made possible via Interfaces. In short, an interface is a type that you declare that has methods it contains, but not their implementation. In order for an object to be of that type, it must implement the interface and all it’s methods. Here’s a classic example:
public interface Animal{
String speak();
}
You’ll notice that this is slightly different from a class. The method speak
has no body. Instead it is declared like a variable. When looking at this you should think, “The type Animal
has the method speak
, which returns a String
. To get use out of it, you need to have a class implement the interface.
public class Dog implements Animal{
public String speak(){
return 'Bark!'
}
}
Now we have a Dog
class that implements
Animal
. This tells the code that Dog
is of type Animal
. In order to do so, we had to declare that Dog
implements the Animal
interface and also flesh out the method speak
. If you don’t implement all of the methods in the interface, you’ll get a compilation error:
Dog: Class must implement the public interface method:
String speak() from Animal
Now let’s use our Dog:
public class AnimalChorus{
public static void sing(Animal a){
system.debug(a.speak());
}
}
//in Execute Anonymous
Dog poochy = new Dog();
AnimalChorus.sing(poochy);
//Debug output:
Bark!
The method sing
in AnimalChorus
accepts objects of the type Animal
. Since Dog
implements Animal
, it is accepted as a parameter for the method sing
. However, because sing
accepts an Animal
and not a Dog
, it can only access Animal
methods. Let’s add another method to our Dog.
public class Dog implements Animal{
public String growl(){
return 'grrrr';
}
public String speak(){
return 'Bark!'
}
}
If we try to use the growl method in AnimalChorus
we’ll get an error.
public class AnimalChorus{
public static void sing(Animal a){
system.debug(a.growl());
//This would not compile, throwing the error:
//Method does not exist or incorrect signature: [Animal].growl()
}
}
So you can think of an interface as a contract; an object is free to have its own unique logic as long as it implements the methods of the interface. This grants us flexibility when we write code. Let’s add some other animals to our chorus.
public class Bird implements Animal{
public String speak(){
return 'Tweet!'
}
}
public class Cat implements Animal{
public String speak(){
return 'Meow!'
}
}
public class Cow implements Animal{
public String speak(){
return 'Moo!'
}
}
//in Execute Anonymous
Dog poochy = new Dog();
Bird tweety = new Bird();
Cat kitty = new Cat();
Cow moomoo = new Cow();
AnimalChorus.sing(poochy);
AnimalChorus.sing(tweety);
AnimalChorus.sing(kitty);
AnimalChorus.sing(moomoo);
//Debug output:
Bark!
Tweet!
Meow!
Moo!
By using an interface, the method sing
is decoupled from its dependency on the animals. We could change how all the animals sing or add new animals, thus giving sing
some flexibility in what it does without having to change it at all.
However, with all this flexibility, interfaces themselves are pretty inflexible. If we wanted to add another function to our interface, we have to update EVERY implementation of that interface with that function. It seems like a huge drawback, but in reality you probably won’t find it to be much of an issue. The biggest benefit I’ve found from interfaces comes with testing, which we’ll discuss in the next post!