Mock library for Apex inspired by mockito focused on easy mock setup Supports stubbing as well as verification
Mock are prepared by calling the doInvocation
method of the loli_mock_MockBase
class which implements the loli_mock_IMock
interface(You can also provide your own implementation).
The method name is provided as String
, the parameters as List<Object>
(unfortunately apex does not allow an automatic discovery of the method name and parameters at the moment).
// Implements the interface to mock
public class MyMock extends loli_mock_MockBase implements MyInterface {
public Integer getInteger(String param1, Integer param2) {
// Casting to return type
return (Integer) super.doInvocation('getInteger', new List<Object> {param1, param2});
}
public void doSomething() {
return super.doInvocation('doSomething', new List<Object> {requestedSlotsCount});
}
}
There are two alternatives for stubbing behaviour.
MyMock mock = new MyMock();
mock.when().invocation(mock.getInteger('Hello World', 0)).thenReturn(1);
MyMock mock = new MyMock();
((MyMock) batchActionMock.when().answerFor(1)).getInteger('Hello World', 0);
Important: Although the first option is more clear it does not work for methods returning void
(some apex limits again). Therefore
MyMock mock = new MyMock();
mock.when().invocation(mock.doSomething().thenReturn(new MyException());
leads to a compiler error. In such cases the second alternative will work
MyMock mock = new MyMock();
((MyMock) mock.when().answerFor(new MyException())).doNothing();
You can stub simple types, complex types and exceptions (by providing an instance of the exception as in the example above).
Use thenAnswer
in case you want to execute additional logic while providing the answer.
Answers are provided by implementing the loli_mock_IAnswer
interface e.g.
public class MyAnswer implements loli_mock_IAnswer {
public Object onInvocation(loli_mock_Invocation invocation) {
// doSome stuff
return 1;
}
}
MyMock mock = new MyMock();
mock.when().invocation(mock.getInteger('Hello World', 0).thenAnswer(new MyAnswer());
// OR
((MyMock) mock.when().answerFor(new MyAnswer())).getInteger('Hello World', 0);
Instead of providing concrete arguments/parameters you can also use argument matchers. For example:
mock.when().invocation(mock.getInteger(mock.anyString(), mock.anyInteger()).thenAnswer(1);
// OR
((MyMock) mock.when().answerFor(1)).getInteger(mock.anyString(), mock.anyInteger());
There are matchers for any simple types (Integer, String, Double and the like). For custom classes the anyObject
can be leveraged by casting the response (again some apex limits).
mock.when().invocation(mock.getInteger((String) mock.anyObject(), (Integer) mock.anyObject()).thenAnswer(1);
// OR
((MyMock) mock.when().answerFor(1)).getInteger((String) mock.anyObject(), (Integer) mock.anyObject());
In case a matcher is used all other arguments also need to be matchers. Therefore
mock.when().invocation(mock.getInteger(mock.anyString(), 1).thenAnswer(1);
// OR
((MyMock) mock.when().answerFor(1)).getInteger(mock.anyString(), 1);
leads to a compiler error.
In such cases use the anyValue
matcher (again casting the returned value is required)
mock.when().invocation(mock.getInteger(mock.anyString(), (Integer) mock.anyValue(1)).thenAnswer(1);
// OR
((MyMock) mock.when().answerFor(1)).getInteger(mock.anyString(), (Integer) mock.anyValue(1));
By implementing the loli_mock_IMatcher
interface you can provide your own matcher implementation.
public class MyMatcher implements loli_mock_IMatcher {
public Boolean matches(Object compare) {
// Matcher logic goes here
return true;
}
}
MyMock mock = new MyMock();
mock.when().invocation(mock.getInteger(mock.matcher(new MyMatcher()), mock.anyInteger()).thenReturn(new TestException());
// OR
((MyMock) mock.when().answerFor(1)).getInteger(mock.matcher(new MyMatcher()), mock.anyInteger());
As for stubbing there are two alternatives for verifying behavior
mock.verify().that(mock.getInteger('Hello World', 1)).called(1);
// OR
((MyMock) mock.verify().expectationFor(loli_mock_Expectation.called(1))).getInteger('Hello World', 1);
Again: The first option does not work for methods returning void
You can verify the exact number of invocations, never, at least and at most.
mock.verify().that(mock.getInteger('Hello World', 1)).called(1);
// OR
((MyMock) mock.verify().expectationFor(loli_mock_Expectation.called(1))).getInteger('Hello World', 1);
mock.verify().that(mock.getInteger('Hello World', 1)).never();
// OR
((MyMock) mock.verify().expectationFor(loli_mock_Expectation.never())).getInteger('Hello World', 1);
mock.verify().that(mock.getInteger('Hello World', 1)).atMost(1);
// OR
((MyMock) mock.verify().expectationFor(loli_mock_Expectation.atMost(1))).getInteger('Hello World', 1);
mock.verify().that(mock.getInteger('Hello World', 1)).atLeast(1);
// OR
((MyMock) mock.verify().expectationFor(loli_mock_Expectation.atLeast(1))).getInteger('Hello World', 1);
Mocking has not been tested for all apex objects/methods. Please feel free to report any bugs.