zpontikas
zpontikas

Reputation: 5682

Mocking the Guava CacheLoader callback method

I have been trying to mock the callback with no success. Here is an example code:

public class TestService {
    private UtilService utilService;

    private LoadingCache<String, String> cache= CacheBuilder.newBuilder()
            .build(new CacheLoader<String, String>() {
                public String load(String key) throws Exception {
                    System.out.println("key = " + key);
                    return getKey(key);
                }
            });

    public String getkeyFromCache(String key) throws ExecutionException {
        return cache.get(key);
    }

    @VisibleForTesting
    public String getKey(String key) {
        return utilService.getKey(key);
    }
}

And a test case like this:

@RunWith(MockitoJUnitRunner.class)
public class TestServiceTest {

    public static final String MYKEY = "Mykey";
    @Spy
    TestService testService=new TestService();

    @Before
    public void before() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testCache() throws ExecutionException {
        doReturn(MYKEY).when(testService).getKey(MYKEY);
        String result=testService.getkeyFromCache(MYKEY);
        String result2nd=testService.getkeyFromCache(MYKEY);
        verify(testService,times(1)).getKey(MYKEY);
    }

}

But it looks like it is not invoked

Wanted but not invoked:
testService.getKey("Mykey");
-> at utils.TestServiceTest.testCache(TestServiceTest.java:38)

However, there were other interactions with this mock:
-> at utils.TestServiceTest.testCache(TestServiceTest.java:36)
-> at utils.TestServiceTest.testCache(TestServiceTest.java:37)

    at utils.TestServiceTest.testCache(TestServiceTest.java:38)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.mockito.internal.runners.JUnit45AndHigherRunnerImpl.run(JUnit45AndHigherRunnerImpl.java:37)
    at org.mockito.runners.MockitoJUnitRunner.run(MockitoJUnitRunner.java:62)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

I want to test the cache functionality but not the UtilService one, so I want to mock it. I can't think of a way to use Answer or Captor for this test or I would have.

Upvotes: 2

Views: 2430

Answers (2)

Admit
Admit

Reputation: 4987

Your issue is that you've created the spy yourself. So the callback will be tied to the real method. Allowing Mockito to deal with the object creation solves the issue. (I'm using mockito 1.10.19)

I've refactored your test code:

@RunWith(MockitoJUnitRunner.class)
public class TestServiceTest {

    public static final String MYKEY = "Mykey";
    @Spy
    TestService testService;

    @Test
    public void testCache() throws ExecutionException {
        doReturn(MYKEY).when(testService).getKey(MYKEY);

        String result=testService.getkeyFromCache(MYKEY);
        String result2nd=testService.getkeyFromCache(MYKEY);

        verify(testService).getKey(MYKEY);
    }

}

Note: As you are using the Mockito runner - you are not required to initialize Mockito yourself. Also times(1) is a default value for verifying the call.

Upvotes: 4

Andriy Slobodyanyk
Andriy Slobodyanyk

Reputation: 2085

When getKey() method is invoked inside the load() method, it is called inside TestService class, not through the mock-proxy. That's why

doReturn(MYKEY).when(testService).getKey(MYKEY);

doesn't work. However, you might and should mock UtilService and see that is accessed only once.

Upvotes: 1

Related Questions