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
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); } }
MyControllerTest
@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<Object> {valueOne, valueTwo}); return null; }else{ mocks.recordMethod(this, 'calculateValues', new List<Object> {valueOne, valueTwo}); fflib_MethodReturnValue methodReturnValue = mocks.getMethodReturnValue(this, 'calculateValues', new List<Object> {valueOne, valueTwo}); if (methodReturnValue != null){ if (methodReturnValue.ReturnValue instanceof Exception) { throw ((Exception) methodReturnValue.ReturnValue); } return (Integer) methodReturnValue.ReturnValue; } } return null; } } }
MyInterface
public interface MyInterface{ Integer calculateValues(Integer valueOne, Integer valueTwo); }
MyService
public class MyService implements MyInterface{ public Integer calculateValues(Integer valueOne, Integer valueTwo){ return valueOne + valueTwo; } }
In the above example, MyController
has a dependency of MyInterface
. Now, we could have just easily and validly written our controller like:
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); } }
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!
Great post Jesse!
Thanks! Glad you enjoyed it.
Hi,
Could you please explain a bit more why we have to implement method calculateValues(…) in MockMyService class.
Hey Ringo,
We need to implement that method because that is the code ApexMocks actually runs rather than your actual service implementation. Notice how MockMyService implements our interface as well. This does tow things. It forces our class to match the public methods on the interface and it allows us to use dependency injection to pass this implementation into our controller rather than the actual code we run in normal scenarios. By mocking out this method, we control what that method returns.
The good news is the author of ApexMocks actually has a tool you use to just generate that code. In most scenarios, you will never have to do anything with that piece of code. I was personally interested in how it actually worked, so that is why I wrote this article to explain more on how this is all possible. Most people will never need to worry about all of this if they are just using ApexMocks.
Hi Jesse,
I’m having some issues with a mock test. I have setup all of my interfaces (correctly I assume as all integration tests work as expected). I am finding that even though I am stubbing my selector class nothing seems to be returned. Here is a pastebin to view the code: http://pastebin.com/ACJNH8wF
when the following line in my domain class is called:
Map newAccountMap = new Map(AccountsSelector.newInstance().selectById(newAccountIds));
newAccountMap is always null (I have debugged and ensured that newAccountIds = myAccounts[1].Id)
Any help you can provide would be most appreciated. Thanks!
My guess would be that you need to add a mocked call for the
SObjectType()
method in the SelectorMock. The problem is that the Mocks class also mocks that method, so it is causing issues when actually being called in the code. Try adding something like this to line 33:mocks.when(selectorMock.sObjectType()).thenReturn(Account.SObjectType);
You’re a genius! That worked like a charm. Thanks for the help, it’s really appreciated.
Hi Jesse,
I am new in Salesforce and trying to use ApexMock. It’s a great solution for Apex and you did a great job explaining on this article. However, by following your example, I couldn’t make the connection about the usage of a mocked method and the coverage of the real one by the unit test.
Could you clarify a little more about the code coverage? Or if you are explaining that in another article, could you share the link?
Anyways, thank you by sharing all these knowledge here.
Hi Jesse,
Never mind. I could go a little further on the understanding of the mock implementation and see the coverage of code as well as better understand the usage of start and stop stubbing.
Thanks once more for your article and contribution with this mock framework for Apex.
Hi Jesse,
I am working on a project in which we need to test an APEX REST Service in such a way that no actual data is created in Salesforce during the testcases execution. I am new to APEX Development.
Searching on the web took me to fflib-apex-mocks library which seems to serve the purpose of avoiding the usage of actual test data inserted in the database for testcases execution. I have installed fflib-apex-mocks and fflib-apex-common in my sandbox environment.
So far, I am not able to find out a step-by-step guide as what I need to do if I want to test my existing Salesforce Objects at a unit level and how can I test my APEX Rest Service? From looking at the fflib-apex-common-samplecode it seems that I need to define classes for my Salesforce objects, I am not sure if I am getting this right.
At a higher-level, below are my questions at a higher-level:
1 – I have a Student__c Object already defined in Salesforce, it has some triggers for insert and update which has checks for various fields and I want these triggers to fire when I try an insert or update for the mock object. I want to develop testcases for that, what do I need to do? How do I start?
2 – I have an APEX Rest Service which gets a collection of Teacher__c objects converted to JSON based on the id of a Student__c object provided to it as a query parameter. I have previously developed testcases for APEX Rest Service but that didn’t involved actual objects usage but my current scenario is different as it involves usage of actual objects.
Can you please help me out in getting started?
Thanks,
Haseeb Khan