Restrict Postgres superusers to local only connections
Using ApexMocks is a powerful way to improve the unit testing of your code base and significantly improve the total turnaround time for all your tests as many tests can be done without having to do any DML
ApexMocks is a framework that works with well-designed applications that support dependency injection. However, you have to use it as designed otherwise your mocking results may not work as expected.
I'm going to make the assumption that you are using Apex Mocks in conjunction with the Force.com Enterprise Architecture pattern (aka fflib) as seen on Trailhead here and here
Some common reasons why your mocks aren't working
Note I assume you have at the beginning of your test method:
fflib_ApexMocks mocks = new fflib_ApexMocks();
I also assume you have set up your Application class per guidelines.
You forgot to inject the mock object (service, selector, domain, or unit of work)
- The injection should be done after any mock object stubbing
- For Unit Of Work:
Application.UnitOfWork.setMock(mockUow);
- For Service:
Application.Service.setMock(ISomeService.class,mockService);
- For Selector:
Application.Selector.setMock(mockSelector);
- For Domain:
Application.Domain.setMock(mockDomain);
You are trying to stub a dependency but the code under test is passing arguments that are different than your mocked dependency expects
Example: Assume an object under test (MyClass) calls an Accounts Selector
AccountsSelector mockAccountsSelector = (AccountsSelector) mocks.mock(AccountsSelector.class);
mocks.startStubbing();
mocks.when(mockAccountsSelector.SObjectType()).thenReturn(Account.SObjectType);
mocks.when(mockAccountsSelector.selectById(someIdSet)).thenReturn(someMockAccounts);
mocks.stopStubbing();
Application.Selector.setMock(mockAccountsSelector);
new MyClass().doStuff(); // code under test
The mockSelector is expecting to be passed the contents of someIdSet
(say id0 and id1) but if the MyClass
object actually calls selectById
with a different ID set, the stubbed selector will not return anything to MyClass
's object and results could go haywire.
You are trying to stub a Selector dependency but forgot to stub the SObjectType() method
Using the same example as above
AccountsSelector mockAccountsSelector = (AccountsSelector) mocks.mock(AccountsSelector.class);
mocks.startStubbing();
// stub the SObject() method AND the specific selector method
mocks.when(mockAccountsSelector.SObjectType())
.thenReturn(Account.SObjectType);
mocks.when(mockAccountsSelector.selectById(someIdSet))
.thenReturn(someMockAccounts);
mocks.stopStubbing();
Application.Selector.setMock(mockAccountsSelector);
Your selector class's super class (fflib_SObjectSelector
) requires your selector class to implement method SObjectType()
so your mock selector must stub it.
You are stubbing the wrong methods
Example:
You have a Contacts selector with methods selectById, selectByEmail, selectByPostalCode(). Your code under test calls selectByEmail
but you didn't mock that method (or mocked selectByPostalCode
or selectById
by mistake).
This error comes up often when code is modified after unit testing is done. Imagine once you had some useful code that did ContactsSelector.newInstance().selectById(..)
and you mocked that method in your unit test. Six months later, you go into the code and change the selector to be ContactsSelector.newInstance.selectByEmail(..)
and then rerun your unit tests. They will fail because the wrong method is mocked in the unit test and hence no Contacts will be returned to the code under test.
You forgot to close your stubbing with `stopStubbing`
Example
AccountsSelector mockAccountsSelector = (AccountsSelector) mocks.mock(AccountsSelector.class);
mocks.startStubbing();
// stub the SObject() method AND the specific selector method
mocks.when(mockAccountsSelector.SObjectType())
.thenReturn(Account.SObjectType);
mocks.when(mockAccountsSelector.selectById(someIdSet))
.thenReturn(someMockAccounts);
mocks.startStubbing(); // OOPS! should be stopStubbing()
Application.Selector.setMock(mockAccountsSelector);
The failure to close the stubbing can lead to unpredictable results (and this is a common issue if you are copy-pasting the mocks.startStubbing()
line!)
Note that mocks.doThrowWhen(..)
must be enclosed within a startStubbing-stopStubbing
pair
You are using Apex types as arguments to stubbed methods without a way to test equality
Example: You are mocking a service that receives as input a custom Apex Type
AccountsServiceImpl mockAccountsService = (AccountsServiceImpl)
mocks.mock(AccountsServiceImpl.class);
mocks.startStubbing();
mocks.when(mockAccountsService.execute(myApexTypes)
.thenReturn(somethingUseful);
mocks.stopStubbing();
Application.Service.setMock(mockAccountsService);
new MyClass().doStuff(); // code under test
The issue here that by default, the stubbing framework has no way to test the equality of the collection myApexTypes
against the actual collection of ApexType that you pass in the code under test (i.e. doStuff()
). Note that Primitives/Sobjects can be tested for equality as can collections of primitives or sobjects.
To get around this, you either have to
Implement for class
MyApexType
theequals()
andhashcode()
method - see Apex DocUse a matcher that ignores the contents of the specific collection (defeating somewhat the rigor of your unit test for some use cases). Do this with a stubbing line such as:
mocks.when(mockAccountService.execute((Set<MyApexType> fflib_Match.anyObject()) .thenReturn(somethingUseful);
You are trying to verify that Unit Of Work was passed an expected SObject but you are not verifying properly
This is better covered in this Andrew Fawcett blog post but the upshot is you should verify unit of work registerNew
or registerUpdate
actions using the Matcher sObjectWith
(again, see the blog post for how to do this).
You are trying to use a Matcher but the runtime arg is null
Let's say you are stubbing a service method String result = doStuff(Integer i)
as follows:
AccountsServiceImpl mockAccountsService =
(AccountsServiceImpl) mocks.mock(AccountsServiceImpl.class);
mocks.startStubbing();
mocks.when(mockAccountsService.doStuff(fflib_Match.anyInteger()))
.thenReturn('Foo');
mocks.stopStubbing();
// Inject the mockService
Application.Service.setMock(mockAccountsService);
Now, if your runtime code under test calls doStuff(..)
with a null argument, the mocks.when
fflib_Match.anyInteger()
will return false - that is, the criteria for the mocks.when(..)
will not be satisfied and no stub return value Foo
is supplied back to the code under test. Instead, null
is returned and most likely, your testmethod will die on a null pointer exception.
Your code under test, after calling the mocked object, modified the argument used in the call to a mocked object
ApexMocks captures all method calls and their arguments. If the arguments are non-primitives, these are captured by reference. Thus, when you verify
, you are most likely verifying the arguments as were passed to the mocked object but if the code under test subsequently modified those arguments, the verify will fail because you have also implicitly modified the recorded argument!.
That is, ApexMocks, when it records the method call + arguments doesn't make a copy of those arguments (if non-primitive).
For example, if your code under test does this:
someMockableService.doStuff(someCollection);
someCollection.clear();
and you verify that doStuff(..)
was called with a collection that looked like someCollection
(say, a list of specific Ids), the verify fails because ApexMocks thinks, by the time you verify
, the recorded argument (originally non-empty) is now an empty list!
You can read a bigger example of this in ApexMocks GitHub Issue #78
Additional References
- Dependency injection with Apex Mocks
- Apex Mocks Readme - many useful links to other blogs
- Force.Com Enterprise Architecture Second Edition Chapter 12 (note that code examples therein that state
mocks.factory(..)
are out of date and should bemocks.mock(..)