ApexMocks: How Does It Work?

Last week, I wrote an article about writing real unit tests on the Force.com platform for the first time using ApexMocks. What Paul Hardaker (@comic96) has been able to do using ApexMocks is a big step for Force.com developers. We now have the capability of testing singular units of code quickly. I have already discussed why this is useful, but how does it all work?

Powered by Dependency Injection

ApexMocks heavily utilizes dependency injection, or the idea that you pass dependencies into a class through some means, typically the constructor. Let’s look back at our final set of code from last time:

MyController

[java]
public class MyController{
private MyInterface myImplementation;
public Integer valueOne{get;set;}
public Integer valueTwo{get;set;}

public MyController(MyInterface myImplementation){
this.myImplementation = myImplementation;
}

public Integer calculatedTotalValue(){
return myImplementation.calculateValues(valueOne, valueTwo);
}
}
[/java]

MyControllerTest

[java]
@isTest
public class MyControllerTest{
public static testMethod void testCalculateValues(){
fflib_ApexMocks mocks = new fflib_ApexMocks();
MyInterface mockMyService = new MockMyService(mocks);

mocks.startStubbing();
mocks.when(mockMyService.calculateValues(5, 3)).thenReturn(8);
mocks.stopStubbing();

Test.startTest();
MyController controller = new MyController(mockMyService);
controller.valueOne = 5;
controller.valueTwo = 3;
Integer totalValue = controller.calculatedTotalValue();
Test.stopTest();

System.assertEquals(8, totalValue, ‘The service should return 8’);
}

private class MockMyService implements MyInterface{
private fflib_ApexMocks mocks;

public MockMyService(fflib_ApexMocks mocks){
this.mocks = mocks;
}

public Integer calculateValues(Integer valueOne, Integer valueTwo){
if (mocks.Stubbing){
mocks.prepareMethodReturnValue(this, ‘calculateValues’, new List {valueOne, valueTwo});
return null;
}else{
mocks.recordMethod(this, ‘calculateValues’, new List {valueOne, valueTwo});
fflib_MethodReturnValue methodReturnValue = mocks.getMethodReturnValue(this, ‘calculateValues’, new List {valueOne, valueTwo});

if (methodReturnValue != null){
if (methodReturnValue.ReturnValue instanceof Exception)
{
throw ((Exception) methodReturnValue.ReturnValue);
}

return (Integer) methodReturnValue.ReturnValue;
}
}

return null;
}
}
}
[/java]

MyInterface

[java]
public interface MyInterface{
Integer calculateValues(Integer valueOne, Integer valueTwo);
}
[/java]

MyService

[java]
public class MyService implements MyInterface{
public Integer calculateValues(Integer valueOne, Integer valueTwo){
return valueOne + valueTwo;
}
}
[/java]

In the above example, MyController has a dependency of MyInterface. Now, we could have just easily and validly written our controller like:

[java]
public class MyController{
private MyService myImplementation;
public Integer valueOne{get;set;}
public Integer valueTwo{get;set;}

public MyController(){
this.myImplementation = new MyService();
}

public Integer calculatedTotalValue(){
return myImplementation.calculateValues(valueOne, valueTwo);
}
}
[/java]

The problem here is that the MyController needs to know what service is being used. This type of architecture provides poor flexibility. By removing that dependency and using an Interface and injecting an instance of a class implementing that interface, we have provided the flexibility to write any number of service implementations. You can see the usefulness of this type of approach in something like a payment service.

Injecting Mocked Dependency

By utilizing dependency injection, we can create our own service purely for testing. In our unit test above, we create our own mocked service as part of the unit test. Lines 21-48 show our MyMockService and how it implements the necessary interface. This allows us to completely bypass the actual service calculations and run our own code. This is where ApexMocks begins to come into play. Specifically, notice how we inject an instance of fflib_ApexMocks into MyMockService at line 5. To allow our class to compile, we have to adhere to the interface. This forces us to add a calculateValues(Integer valueOne, Integer valueTwo) method at line 28. You can see in the method that the fflib_ApexMocks instance we injected earlier is now heavily utilized. One of the beauties of open source software is that you can really review the code you are interacting with. In this case, I heavily suggest you to review the fflib_ApexMocks class, fflib_MethodReturnValue class, and fflib_MethodReturnValueRecorder class as they are all heavily utilized in our example.

Our calculateValues method is written so that it:

  • Records the method and result when stubbing is turned on – lines 29 – 31
  • Retrieving the mocked value when stubbing is turned off – lines 33-46

The way stubbing works is that it essentially stores the mocked parameters and result in a Map on the fflib_MethodReturnValueRecorder class (check the prepareMethodReturnValue method). This is saved (and ultimately returned as) an instance of the fflib_MethodReturnValue class. When stubbing is turned off, the method returns the fflib_MethodReturnValue class from the Map. Note this instance of the fflib_MethodReturnValueRecorder lives on the fflib_ApexMocks instance of MyMockService that we pass in all the way back at line 5 in MyControllerTest. This allows us to make sure it all stays in memory as our test runs.

Mocks in Action

Let’s inspect the actual unit test now. Lines 7-9 is where we actually run the stub out our mock. We turn on stubbing at line 7. This is important because it needs to be turned on to put our mocked value into our Map when calculateValues() gets mocked out at line 8. When line 8 is executed, it actually runs the MockMyService.calculateValues() method as parameter of the when() method. This will run through lines 29-31 on MyMockService, creating a reference in the Map on the fflib_MethodReturnValueRecorder. As part of this as well, the fflib_MethodReturnValue generated from the when() method gets its value set as part of the thenReturn() method. This is then set on the fflib_ApexMocks instance. Following immediately after this step, line 9 turns stubbing back off. This allows the controller to then call the MyMockService.calculateValues when MyController.calculatedTotalValue() is called at line 15 and simply return the value we set at line 8. It completely removes the need to run MyService code at all because it is simply never used.

True Unit Tests

I hope this helped reinforce the point that ApexMocks truly allows you to unit test. You will only test MyService when you write unit tests for it. It will not be part of the MyController test because the code simply never runs. Now, for this simplistic example, this approach is obviously overkill, but hopefully you can see the power something like this can provide. You don’t need to generate the entire data model, allowing all of the triggers, workflow rules, etc to run. Mock those service calls and enjoy fast, reliable unit tests.

Conclusion

This is only a small piece of what ApexMocks has to offer. It is a complex library that does some really cool stuff. Take some time to go through the code to see what is possible and how it works. Next week I’ll focus on helping you set up ApexMocks in your own org. Enjoy!

Advertisement

Go to Smartblog Theme Options -> Ad Management to enter your ad code (300x250)

Comments are closed.