I want to mock a query provided on my repository like this:
#Test
public void GetByEmailSuccessful() {
// setup mocks
Mockito.when(this.personRepo.findAll()
.stream()
.filter(p -> (p.getEmail().equals(Mockito.any(String.class))))
.findFirst()
.get())
.thenReturn(this.personOut);
Mockito.when(this.communityUserRepo.findOne(this.communityUserId))
.thenReturn(this.communityUserOut);
...
My #Before method looks like this:
#Before
public void initializeMocks() throws Exception {
// prepare test data.
this.PrepareTestData();
// init mocked repos.
this.personRepo = Mockito.mock(IPersonRepository.class);
this.communityUserRepo = Mockito.mock(ICommunityUserRepository.class);
this.userProfileRepo = Mockito.mock(IUserProfileRepository.class);
}
Sadly when I run the test I receive the error:
java.util.NoSuchElementException: No value present
When I double-click the error it points at the .get() method of the first lambda.
Have any of you successfully mocked a lambda expression and know how I can solve my problem?
There's no need to mock such deep calls. Simply mock personRepo.findAll() and let the Streaming API work as normal:
Person person1 = ...
Person person2 = ...
Person person3 = ...
List<Person> people = Arrays.asList(person1, person2, ...);
when(personRepo.findAll()).thenReturn(people);
And then instead of
.filter( p -> (p.getEmail().equals(Mockito.any(String.class))) )
just set/mock email on your Person objects to be the expected value.
Alternatively, consider implementing PersonRepo.findByEmail.
Two things:
Mockito.when(this.personRepo.findAll()
.stream()
.filter(p -> (p.getEmail().equals(Mockito.any(String.class))))
.findFirst()
.get())
.thenReturn(this.personOut);
First, you're trying to mock a chain of five different method calls. Mockito doesn't handle this very well; though the RETURNS_DEEP_STUBS answer (if put on personRepo) would save and return stub objects where applicable, each call to when will itself stub exactly one call.
Second, Mockito matchers aren't flexible enough to work deeply in calls; calls to when should contain exactly one method call without chaining, and calls to Mockito matchers like any should stand in for exactly one of the arguments in that method. The way you have it, you're creating a predicate p -> (p.getEmail().equals(null)) and leaving a matcher on the stack to break things later.
Use Alex Wittig's answer to solve this problem, and be mindful of stubbing and using matchers correctly in future problems.
Related
I have a basic unit test for a Service with a mocked repository as follow :
#Test
public void deleteEmployeeCallsRepositoryDeleteById() {
Employee employee = new Employee();
employee.setName("coolName");
employee.setId(8978L);
EmployeeRepository repoSpy = spy(employeeRepository);
service.deleteEmployeeById(employee.getId());
verify(repoSpy, atLeastOnce()).deleteById(employee.getId());
}
When running the test, I get the following error :
Wanted but not invoked:
employeeRepository.deleteById(8978L);
-> at unit.services.EmployeeServiceImplTests.deleteEmployeeCallsRepositoryDeleteById(EmployeeServiceImplTests.java:70)
However, there was exactly 1 interaction with this mock:
employeeRepository.deleteById(8978L);
-> at services.EmployeeServiceImpl.deleteEmployeeById(EmployeeServiceImpl.java:34)
Second sentence saying the exact opposite of the first sentence...
I tried using verify(repoSpy, atLeastOnce()).deleteById(anyLong()); just in case, but same error message.
spy doesn't alter the argument in-place, it decorates it and returns the spying decorator. You need to pass mocks or spies to the code under test in the ordinary manner.
I have a method signature like
public void add(byte[] key, Optional<Byte[]> secondaryKey) { ... }
My test looks something like
byte[] key = "testKey".getBytes();
byte[] secondaryKey = "secondaryKey".getBytes()
//call the method that internally calls add()
Mockito.verify(mockClass).add(key, Optional.of(ArrayUtils.toObject(secondaryKey))
The verification always fails in this case saying wanted parameters are different from actual. I have a similar add method that just take byte[] key as input parameter. Test on that method succeeds. So I think there is something wrong with the way I am trying to match Optional parameter here.
The Optional does not perform a deepEquals, therefore the equality check will fail considering that you have passed an Byte[] into the Optional.
You can see this bug report from a user that faced a similar issue. JDK-8075723
You will probably want to leverage some ArgumentMatchers to compare the arguments passed into your mock.
Since you have an Optional being passed through, you can unwrap that object using ArgumentMatchers.argThat which requires you to implement a ArgumentMatcher.matches method.
Mockito.verify(mockClass).add(ArgumentMatchers.eq(key), ArgumentMatchers.argThat(r -> {
return r.isPresent() && Objects.deepEquals(secondaryKey, r.get())
));
Edit:
You can also use ArgumentCaptor if you prefer to capture the state of the parameters passed into the mocks and perform assertions.
ArgumentCaptor<Optional> captor =ArgumentCaptor.forClass(Optional.class)
verify(pojo).add(eq(key), captor.capture());
Optional<byte[]> result = captor.getValue();
assertTrue(result.isPresent());
assertArrayEquals(secondaryKey, result.get());
I have a query statement which returns a PaginatedQueryList and I assign it to a List. Then I operate on the list.
List<MyClass> entries= mapper.query(MyClass.class, queryExpression);
//Logic to test
Map<String, Map> map = entries.stream()
.collect(Collectors.toMap(MyClass::getA, MyClass::getB));
....
....
I tried to test it like
when(mapper.query(eq(MyClass.class), any(DynamoDBQueryExpression.class)))
.thenReturn((PaginatedQueryList<MyClass>) list);
where list is List<MyClass>(ArrayList) that I construct myself and want to pass it to the main program to test the main logic.
But it throws
java.lang.ClassCastException: java.util.ArrayList cannot be cast to com.amazonaws.services.dynamodbv2.datamodeling.PaginatedQueryList
How can I send the list when it executes mappper.query(..).
The motive is to send the list that I constructed to entries when executing mapper.query(..).
NOTE:
I also tried sending a mocked PaginatedQueryList but when it does entries.stream() I want to send the actual stream.
I tried like
when(paginatedQueryList.stream()).thenReturn(Stream.of(list.get(0), list.get(1)));
It throws
java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
NOTE 2:
I use entries.stream() twice in my logic to test
Mockito has some powerful tools for this.
List<MyClass> list = new ArrayList<>();
when(mapper.query(eq(MyClass.class), any(DynamoDBQueryExpression.class)))
.thenReturn(mock(PaginatedQueryList.class, withSettings().defaultAnswer(new ForwardsInvocations(list))));
This will return a mocked PaginatedQueryList which forwards all method calls to list.
I was able to solve the problem by sending mocked PaginatedQueryList and then send list.stream() twice (for each access of entries.stream()).
when(paginatedQueryList.stream()).thenReturn(list.stream()).thenReturn(list.stream());
Any better approaches are welcome.
My answer is a generic solution for mocking PaginatedQueryList in tests (might be useful for googlers, since there's not much answers for this topic).
For my test this worked:
// given
List<UserDevice> expectedDevices = Arrays.asList(generateUserDevice(USER_ID, DEVICE_ID), generateUserDevice(USER_ID, "deviceId2"));
PaginatedQueryList listMock = mock(PaginatedQueryList.class);
when(listMock.listIterator()).thenReturn(expectedDevices.listIterator());
when(mapper.query(eq(UserDevice.class), any(DynamoDBQueryExpression.class))).thenReturn(listMock);
// when
List<UserDevice> devices = dao.findAll(USER_ID);
// then
assertEquals(expectedDevices, devices);
so, as you can see, I mocked the PaginatedQueryList and mocked its listIterator() method.
I am trying to mock a DAO layer Interface which looks something like this ...
public interface DummyDAO{
public List<VO> getSearchCriteria(String Id, Integer version) throws Exception;
}
In the implementation part, I run a query, and it fetches me a list of Value objects based on that query. How do I mock a List of VO's coming from a database. Is there a simple way to do this ?. I am trying to encourage my team to use Jmockit but I am still a newbie to this mocking tool. If you need more info please let me know.
Thanks
The answer here can depend partly on how the CUT (code under test) is using the List<VO>. For example, if your code is a RESTful service merely acting as a proxy and returning this List<VO> to the user, without examining the contents or anything, then this would suffice:
#Tested CUT cut;
#Injectable DummyDAO dao;
#Injectable List<VO> res;
#Test
public void testSomething() throws Exception {
new Expectations() {{
dao.getSearchCriteria(anyString, anyInt); result = res;
}};
List<VO> output = cut.methodBeingTested();
// assertions go here, like that output and res are the same
// you can also put Verifications here
}
If you are expecting to do analysis of the VOs then things can get more complex, like for example:
#Tested CUT cut;
#Injectable DummyDAO dao;
#Mocked VO vo;
#Test
public void testSomething() throws Exception {
final List<VO> res = new ArrayList<VO>();
Collections.addAll(res, vo, vo, vo, vo);
new Expectations() {{
dao.getSearchCriteria(anyString, anyInt); result = res;
}};
new Expectations(4) {{
vo.getFoo(); returns(5, 47, 13, -7);
}};
cut.methodBeingTested();
// assertions go here, like that you handled the negative number properly
// you can also put Verifications here
}
Notice here I did not mock the List -- if you are doing more complex operations it's better to stick with a real List and not have to mock all of its operations. I mock a single VO and add it to the list multiple times--this is OK because each time you examine it it will act differently and thus seem to be a different VO.
The examination, because it is happening 4 times, I moved to a different Expectations block since you are expecting it to happen 4 times. I'm just doing this to show off the expectations block since in this case it could be just as easily accomplished by having vo.getFoobar(); times=4; returns(5, 47, 13, -7); in the original block... alternatively, this second block could look like
new StrictExpectations(4) {{
vo.getFoo(); returns(5, 47, 13, -7);
vo.getBar(); returns("foo", "bar", "baz", "quux");
}};
In this case, the times field would not work and you'd have to have it in a separate block - this block is saying it expects to have getFoo() and getBar() called, alternating, exactly 4 times--as you would want to do if iterating through a list and looking at both properties each time.
I hope I have given you food for thought; without knowing more of your specific use case I can't be more specific myself.
One simple way to do that is using Mockito.spy() .The spy method create a proxy of your instance that give to you the opportunity to simulate the behavior of one method.
You could create one list that will be returned when you call your dao method.
Example:
yourDao = spy(new YourDaoImplementation())
doReturn(yourExampleList).when(yourDao).getSearchCriteria(any(String.class), any(Integer.class));
If you really want to test your dao, you could use a light weight data base as HSQLDB
I want to use mockito spy.
When I set a return value in both following ways:
when(imagesSorterSpy.sortImages(imagesAsInsertionOrder, user)).thenReturn(imagesSorterSpy.sortImages(imagesAsInsertionOrder, user, fakeNowDate));
doReturn(imagesSorterSpy.sortImages(imagesAsInsertionOrder, user, fakeNowDate)).when(imagesSorterSpy).sortImages(imagesAsInsertionOrder, user);
I see the return value is being evaluated eagerly
meaning when this "setting" line is executed.
how can i force the spy to evaluate the return value only on demand?
meaning when the "when" condition is met.
update
Thanks to #RobbyCornelissen I have tried this code:
when(imagesSorterSpy.sortImages(imagesAsInsertionOrder, user)).thenAnswer(new Answer() {
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
ImagesSorter mock = (ImagesSorter)invocation.getMock();
return mock.sortImages((List<Image>)args[0], (UserInfo)args[1], fakeNowDate);
}
});
But it didn't help:
1) the "when" expression was invoked immediately. (not wanted)
2) eventually the callback wasn't call.
First let me warn you on partial mocks, because that is what the code is actually doing, it's wrong design wise. It may be more relevant to use a strategy pattern to compose behavior of the tested subject. Mockito team (including me) strongly advises to stay away of partial mocks whenever possible.
EDIT : I don't know the code and I don't know exactly which component under test but from what I gather there's a type responsible to sort images, let's call it ImagesSorter.
So first case ImagesSorter is a dependency of a test subject, so in this case just stubbing the mock of ImagesSorter will do.
If however it is ImagesSorter itself under test, and stubbing a special method of this class is called a partial mock and it is plain wrong. It exposes internal of the production code in the test. So there's several solutions.
As the code snippet showed in the answer shows a fakeDate, one of the solution is to not use things like new Date() and code a simple class TimeSource whose sole responsibility is to provide a date. And in tests the bwhavior of this TimeSOurce could be overriden.
A simplier solution would be to use JodaTime as it provides this functionality built in.
If the scope of test goes beyond changing the date, then maybe ImagesSorter needs a way to be configured with other objects. Inspiration on how to do it can be found with the cache builder of guava. If the configuration is dead simple then a simple constructor coud do it.
That could look like :
class ImagesSorter {
ImagesSorterAlso algo;
ImagesSorter(ImagesSorterAlgo algo) { this.algo = algo; }
Iterable sortImages(...) {
algo.sort(...);
}
}
interface ImagesSorterAlgo {
Iterable sort(...);
}
Now about your questions :
1) the "when" expression was invoked immediately. (not wanted)
It is expected imagesSorterSpy is a spy so by default it calls the real code. Instead you should use the alternate API, the same that #RobbyCornelissen showed. i.e.
doAnswer(sortWithFakeDate()).when(imagesSorterSpy).sortImages(imagesAsInsertionOrder, user);
// with BDD aliases (BDDMockito) which I personnaly finds better
willAnswer(sortWithFakeDate()).given(imagesSorterSpy).sortImages(imagesAsInsertionOrder, user);
will(sortWithFakeDate()).given(imagesSorterSpy).sortImages(imagesAsInsertionOrder, user);
sortWithFakeDate() would a static factory method that returns the answer, so the code reads well, and maybe reused elsewhere.
2) eventually the callback wasn't call.
This issue is most probably due to non equal arguments. You may need to check the equals method. Or relax the stub using the any() matcher.
I don't know the types of the arguments and classes you're using, so I can't provide a complete example, but you can stub using callbacks with the Answer<T> interface:
Mockito.doAnswer(new Answer() {
Object answer(InvocationOnMock invocation) {
ImagesSorter mock = (ImagesSorter) invocation.getMock();
Object[] args = invocation.getArguments();
return mock.sortImages((List<Image>) args[0], (UserInfo) args[1],
fakeNowDate);
}
}).when(imagesSorterSpy).sortImages(imagesAsInsertionOrder, user);