简体   繁体   中英

how to use mockito spy for lazy evaluation?

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 .

  1. So first case ImagesSorter is a dependency of a test subject, so in this case just stubbing the mock of ImagesSorter will do.

  2. 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.

    1. 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.

    2. 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. ie

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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM