I am trying to write a Unit Test for the following service method:
public CommandDTO update(UUID uuid, QuantityRequest request) {
Quantity quantity = quantityRepository.findByUuid(uuid)
.orElseThrow(() -> new EntityNotFoundException(QUANTITY));
Quantity updated = saveQuantity(quantity, request);
return CommandDTO.builder().uuid(updated.getUuid()).build();
}
private Quantity saveQuantity(Quantity quantity, QuantityRequest request) {
//map fields (code omitted for brevity)
return quantityRepository.save(quantity);
}
I use ArgumentCaptor so that I catch the quantity parameter in the private method that my service method calls: quantityRepository.save(quantity).
#Test
public void test() {
Quantity quantity = new Quantity();
QuantityRequest request = new QuantityRequest();
request.setQuantity(100);
when(quantityRepository.findByUuid(uuid)).thenReturn(Optional.of(quantity));
// It seems to be meaningless this stubbing. because I already stb it in verify method below
when(quantityRepository.save(any())).thenReturn(quantity);
quantityService.update(uuid, request);
verify(quantityRepository).save(quantityCaptor.capture());
Quantity captured = quantityCaptor.getValue();
// assertions...
}
The test is working, but if I remove when(quantityRepository.save(any())).thenReturn(quantity); line, it throws "null pointer exception error" because in this case updated parameter in the update method is null. So, do I have to use the mentioned stubbing in the when() method? I think I do not need and the verify already perform that task via verify(quantityRepository).save(quantityCaptor.capture()). Is that true?
The problem lies in the following lines:
Quantity updated = saveQuantity(quantity, request);
return CommandDTO.builder().uuid(updated.getUuid()).build();
Which is essentially the same as:
Quantity updated = quantityRepository.save(quantity)
return CommandDTO.builder().uuid(updated.getUuid()).build();
The stubbing is necessary, because you're expecting the save method to return something, when you call updated.getUuid().
Without the stub, updated is null and your call results in a NullPointerException.
No, you need the stubbing here. You can't delete the when(save) call because your test depends on what return value you have for save. However, you're on the right track to question whether any given thing needs to be both stubbed and verified.
You're right that it's often redundant to verify things that you've stubbed, and the docs to verify tell you so:
Although it is possible to verify a stubbed invocation, usually it's just redundant. Let's say you've stubbed foo.bar(). If your code cares what foo.bar() returns then something else breaks(often before even verify() gets executed). If your code doesn't care what foo.bar() returns then it should not be stubbed.
And as attributed to Aaron Jensen, quoted in "Asking and Telling" by Mockito's original author Szczepan Faber:
If you’re verifying you don’t need to stub unless of course that method returns something that is critical to the flow of your test (or code), in which case you don’t really need to verify, because the flow would have verified.
In general, if you've stubbed something, then you make your test assertions at the end and you don't need to test that the method was called—the assertions would fail. And if you verify that a method was called but nobody cares about the result—or the result is passed directly back to the caller—then you might not need to stub, because Mockito's default return values like 0 and null should work fine.
Here, neither of those are true: A default null value causes an NPE, and you need to verify because that's the only supported way to get a value out of your ArgumentCaptor. This means that you call verify, but not for the sake of actual verification, but rather to get the value out of ArgumentCaptor. Part of the verify call is redundant, but there isn't another practical way to get to the necessary ArgumentCaptor part, so your code is fine with both when and verify.
Related
I have a class which contains the following three methods:
void add(Service... objs)
void add(Collection<Service> objs)
void add(Stream<Service> objs)
As you might expect, these all support the adding of zero or more objects, which can be specified individually or as part of an array, a collection or a stream. The first two variants create a stream from their arguments, and pass them to the third variant which actually does the adding.
In testing an object that uses this class, I have created a Mockito mock object to represent an instance of this class, using Spring's #MockBean annotation. I can see in the debugger that the object under test contains the mock object, and that the call I am expecting (with a single argument of type Service) is being addressed to the mock. Because the method that should be called is the first variant (the varargs one), and I know that varargs parameters are a little tricky, I coded the test to check that the mock is called with the correct parameter as follows:
ArgumentCaptor<Service> captor = ArgumentCaptor.forClass(Service.class);
verify(theMock).add(captor.capture());
assertThat(captor.getAllValues()).containsExactly(expectedService);
However, when I run this code the assertion fails because the List returned by captor.getAllValues() contains not a Service, but a Stream: the failure message says:
java.lang.AssertionError:
Expecting:
<[java.util.stream.ReferencePipeline$Head#2cfe272f]>
to contain exactly (and in same order):
<[com.xxx.data.Service#37c5]>
but some elements were not found:
<[com.xxx.data.Service#37c5]>
and others were not expected:
<[java.util.stream.ReferencePipeline$Head#2cfe272f]>
When I run the code in the debugger, I can see that the call from the object under test to add(Service...) invokes the real implementation; this invokes add(Stream<Service>) and it is that call which is intercepted by the mock. That explains why I am seeing the failure, but I don't understand why the mock is failing to intercept the original call, or what I can do to make it do so.
Update your ArgumentCaptor to accept Service[]
ArgumentCaptor<Service[]> serviceCaptor = ArgumentCaptor.forClass(Service[].class);
And assert
Service[] actualServices = serviceCaptor.getAllValues();
assertEquals(actualServices.length, 1);
assertEquals(actualServices[0], service);
And it's best practice to use ErrorCollector in Junit to assert more than one
assert and SoftAssect in Testng and call after your assertion softAssert.assertAll()
I have come up with a workaround to the problem, but the actual problem still exists and I think it is probably a Mockito bug (raised as https://github.com/mockito/mockito/issues/1929).
The workaround is to add this method to my test class. I've added a generic method because it is not just a call to the add() method that has the problem, but also a call to a similar overloaded remove() method that takes String arguments.
private <T, V> void verifyCall(T mock, BiConsumer<T, V> call,
V expectedArg, Class<V> type)
{
ArgumentCaptor<V> captor = ArgumentCaptor.forClass(type);
call.accept(verify(mock), captor.capture());
List<?> values = captor.getAllValues();
try {
assertThat(values.get(0)).isEqualTo(expectedArg);
} catch (AssertionFailedError ex) {
assertThat((Stream<V>) values.get(0)).containsExactly(expectedArg);
}
}
This should work whether the call intercepted by the mock was to the varargs variant of the method (as it should be) - in which case the assertion in the body of the try block will not throw an exception - or to the Stream variant (as it currently is) - in which case the assertion in the body will throw an exception and the assertion in the catch block will be executed.
Then, when I want to verify that my mock's add() method was called with the expected Service object, I do so with:
verifyCall(theMock, Datastore::add, expectedService, Service.class);
And similarly, for the remove() method:
verifyCall(theMock, Datastore::remove, expectedDeletedKey, String.class);
Very pleasingly, when I finally got this working the test failed because I had made a mistake in the method under test. Which made it all worthwhile.
D'oh. I failed to spot that the two varargs methods were declared as final. Removed this and it all works as expected.
How would I mock methods that accept a lambda using Mockito so that I am able to control which arguments are passed into the callback? I am specifically trying to mock the JDBI method useExtension which is used like this:
jdbi.useExtension(OrgUnitDao.class, dao -> {
// Skip if already loaded
// Skip if already loaded
if (dao.orgUnitsAreLoaded()) {
I would like to substitute the dao object passed back into the callback so that I could control the branching using the return value of dao.orgUnitsAreLoaded().
The signature looks like this
public <E,X extends Exception> void useExtension(Class<E> extensionType,
ExtensionConsumer<E,X> callback)
throws NoSuchExtensionException,
X extends Exception
This is the full answer to my question. It's simplified down to the very basics of how to do the stubbing and so doesn't reflect the production code I am to test, but it shows exactly the mechanics needed to do it.
final Jdbi jdbi = mock(Jdbi.class);
doAnswer(invocation -> {
System.out.println("this is the doAnswer lambda - just setting up the answer and the mocks");
final Class<OrgUnitDao> daoClass = invocation.getArgument(0);
final ExtensionConsumer callback = invocation.getArgument(1);
final OrgUnitDao mock1 = mock(daoClass);
when(mock1.orgUnitsAreLoaded()).thenReturn(false);
// call the actual callback method
callback.useExtension(mock1);
return null;
}).when(jdbi).useExtension(eq(OrgUnitDao.class), any());
// This is the method call I am to test
// Regard this as hidden away in some outer method in
// the System-Under-Test, but that I have been able
// to inject all its dependencies
jdbi.useExtension(OrgUnitDao.class, new Foo());
/// Further down, outside of the method
// Only replaced the lambda with this to get toString() for debugging ...
class Foo implements ExtensionConsumer<OrgUnitDao, RuntimeException> {
#Override
public void useExtension(OrgUnitDao orgUnitDao) throws RuntimeException {
System.out.println("A real method call, now using the passed in mocked dao:" + orgUnitDao.orgUnitsAreLoaded());
}
#Override
public String toString() {
return "OrgUnitDao class";
}
}
To parallel the conversation on the question "Calling callbacks with Mockito", your lambda might be called synchronously during the execution of your method-under-test, or it might be called later based on some external factor or interaction. Like Dawood's answer there, your answer here using a Mockito Answer will work, and is the only way to go if you are looking for the synchronous style (where mockJdbi calls your lambda immediately before methodUnderTest returns). If your lambdas are going to be called asynchronously, or if your system tolerates you calling the lambda asynchronously, you might want to test the state after your method-under-test returns but before you interact with the lambda.
// MockitoJUnitRunner, MockitoRule, or MockitoAnnotations.initMocks populate these.
// Especially useful for the ArgumentCaptor's generic arguments.
#Mock Jdbi mockJdbi;
#Mock OrgUnitDao mockOrgUnitDao;
#Captor ArgumentCaptor<ExtensionConsumer<OrgUnitDao, RuntimeException>>
extensionConsumerCaptor;
#Test public void yourTest() throws Exception {
// No stubbing needed! Just create the system under test.
YourSystemUnderTest systemUnderTest = new YourSystemUnderTest(mockJdbi);
// Call the method under test, which presumably calls useExtension(...).
systemUnderTest.methodUnderTest();
// Assert anything that should be true before the lambda is called.
assertFalse(systemUnderTest.getSomeState());
// Confirm that useExtension was called, and simultaneously acquire the lambda.
// ArgumentCaptor.capture() is a matcher, so every argument requires a matcher like eq.
verify(mockJdbi).useExtension(eq(OrgUnitDao.class), extensionConsumerCaptor.capture());
// Prepare the mock DAO and call the lambda.
when(mockDao.getFoo()).thenReturn("bar");
extensionConsumerCaptor.getValue().useExtension(mockDao);
// Assert anything that should be true after the lambda is called.
assertTrue(systemUnderTest.getSomeState());
}
Though lambdas reduce the boilerplate previously associated with anonymous inner classes, you may also prefer using the Captor style because it saves you from creating lengthy Answer implementations and hiding your test assertions or Mockito verifications in them. This is especially tempting if your project prefers BDD-style mocks with clear "given-when-then" structure (though my example more-closely resembles "given-when-then-when-then").
Collection<T_SI_IDABAREME> tSiIdabaremes;
DAO_F_IDA_DESC mockDaoFIdaDesc
prepareExpects(){
expect(mockDaoTSiIdabareme.findByDate(isA(Date.class)))
.andReturn(searchByParameter(tSiIdabaremes, date));
}
Is it possible to use the date that will be passed to the findByDate in the andReturn?
PS: This is a service test class and I'm doing it in a way to bypass the database.
whenever you use expect method like this
Easymock.expect(someMethod(Date.Class)).andReturn(something);
you are instructing compiler to mock all calls to that method whenever ANY object of Date class is passed as parameter,and you will not be able to use that object in return expression.
on the other hand if you have something like this,
Easymock.expect(someMethod(someSpecificDateObject)).andReturn(someSpecificDateObject);
you are instructing the compiler to mock this method call ONLY when a specific object of Date class is passed as parameter(someSpecificDateObject in this case) and you will be able to use this parameter while returning, because you know that method gets mocked only when this object is passed.
You can use second option if it is favourable to you,but with first option what you ask is not possible.
Hope this helps!
Good luck!
Instead of:
expect(mockDaoTSiIdabareme.findByDate(isA(Date.class)))
.andReturn(searchByParameter(tSiIdabaremes, date));
I should have put
expect(mockDaoTSiIdabareme.findByDate(isA(Date.class)))
.andAnswer(new IAnswer<Collection<T_SI_IDABAREME>>() {
public Collection<T_SI_IDABAREME> answer() throws Throwable {
return searchByParameter((Date)getCurrentArguments()[0]);
}
}
);
Which will only look for the return value when the method is being exectued and then we can use getCurrentArguments() to retrieve the arguments passed to the method.
You can find more about this in the EasyMock Documentation.
I am new to Mockito, I am trying to verify the attributes of an object which gets created inside a method.
pseudo code below:
class A{
...
public String methodToTest(){
Parameter params = new Parameter(); //param is basically like a hashmap
params.add("action", "submit");
return process(params);
}
...
public String process(Parameter params){
//do some work based on params
return "done";
}
}
I want to test 2 things:
when I called methodToTest, process() method is called
process() method is called with the correct params containing action "submit"
I was able to verify that process() is eventually called easily using Mockito.verify().
However trying to check that params contains action "submit" is very difficult so far.
I have tried the following but it doesn't work :(
BaseMatcher<Parameter> paramIsCorrect = new BaseMatcher<Parameter>(){
#Overrides
public boolean matches(Object param){
return ("submit".equals((Parameter)param.get("action")));
}
//#Overrides description but do nothing
}
A mockA = mock(A);
A realA = new A();
realA.methodToTest();
verify(mockA).process(argThat(paramIsCorrect))
Any suggestion ?
If you have got verify() to work, presumably it is just a case of using an argument matcher to check the contains of params.
http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html#3
The example given in the above docs is verify(mockedList).get(anyInt()). You can also say verify(mockedList).get(argThat(myCustomMatcher)).
As an aside, it sounds like you are mocking the class under test. I've found that this usually means I haven't thought clearly about either my class or my test or both. In your example, you should be able to test that methodToTest() returns the right result irrespective of whether or not it calls process() because it returns a String. The mockito folk have lots of good documentation about this sort thing, particularly the "monkey island" blog: http://monkeyisland.pl/.
Just pass Parameter in as a constructor argument to a constructor of the class A, then use a mocked instance/implementation of Parameter in your test and verify on the mock. That is how it is normally done - you separate your classes and compose them using constructor injection, that enables you to pass in mocks for testing purposes (it also allows rewiring the application and exchanging some commons a lot easier).
If you need to create Parameter on every function invocation you should use a factory that creates Parameter instances and pass that in. Then you can verify on the factory as well as the object created by the factory.
I am experiencing a certain bug in JUnit/JMock. I am trying to mock a couple of objects and then assert that all expectations is satisfied. I am running a simple test such as :
#Test
public void sellingPutOptionProductDoesNotCauseDisclosure() throws PositionVerificationException, DataLoadException, MissingPriceException {
final OptionProduct optionProduct = setupOptionProduct();
context.assertIsSatisfied();
}
private OptionProduct setupOptionProduct() {
final Option optionProduct = context.mock(Option.class);
context.checking(new Expectations() {
{
oneOf(optionProduct).getUnderlyingProduct();
will(returnValue(new Object()));
}
});
return optionProduct;
}
The Option is an object and I am using Mockery like this:
context = new Mockery() {
{
setImposteriser(ClassImposteriser.INSTANCE);
}
};
If I run the above test I am gettiing Test passed, where JVM does not terminate and the last print out in console is:
Exception in thread "main"
ANy ideas what might be causing this?
I'm a little late to the party, but I just had a similar problem and was able to track down the cause.
Normally, when a method is called that is not part of the expectations, JMock builds the log telling you what was expected that was not found. In my case, it tried to create that log message after encountering an unexpected call. The act of creating the log message threw an exception, which got JMock all confused and it reported that the test passed, when it had actually failed.
In my case, the reason that the exception was thrown was that one of the parameters being passed to the "unexpected" function call was an instance of a class. That class was initialized, in part, with mocked objects. When JMock was trying to build the error message to tell me about the unexpected invocation, it needed to describe the parameter. Usually it will say something like unexpected invocation: myobject.myMethod(param1,param2).
Because param1, in my case, contained member variables that were mock objects, and the param1 class did not define toString(), the default, Object.toString() was used.
Object.toString() is defined as: return getClass().getName() + "#" + Integer.toHexString(hashCode());
My implementation of hashcode for param1 was using some of those mocked objects. Those calls on the mocked objects to calculate the hashcode were 'unexpected' invocations themselves, leading JMock to throw an exception when trying to describe the test failure to me.
Unfortunately, instead of recognizing this condition and still reporting as much of the failure as it could, JMock seemed to give up altogether and report the test as passing and offhandedly mention Exception in thread "main."
To see if this is happening to you, I recommend you check the parameters involved in the offending function calls. If any of them are classes, not interfaces, you should see if their use of equals / hashcode / toString use any suspicious calls to member variables that may not be playing nice with how your scenario is being mocked.