whishky
whishky

Reputation: 416

How to mock an interface which extends another interface

I have an interface

@Repository
public interface WorkJobRepository extends JpaRepository<WorkJob, Long> {

    WorkJob findById(String id);
}

This extends JpaRepository.

Now from some other class ReadWorkJob

public class ReadWorkJob implements WorkJob {

    @Autowired
    private WorkJobRepository;

    public void writeNewIdData(JSONObject jsonObject) {
        WorkJob workJob = getExistingId(jsonObject.get("id").toString());
    }

    private WorkJob getExistingId(String id) {
        logger.info("check point ppppp");
        WorkJob workJob = workJobRepository.findById(id);
        logger.info("check point qqqqq");
        return workJob != null ? workJob : new WorkJob();
    }
}

I am calling this like

workJobRepository.findById(id);

now I want to test the methods in class ReadWorkJob and to do that i want to mock the call to workJobRepository and want it to return null.

@RunWith(MockitoJUnitRunner.class)
public class ReadWorkJobTest {

    private static final String RIGHT_DATA_FILE_NAME = "test.txt";

    @InjectMocks
    private ReadWorkJob readWorkJob;

    private WorkJobRepository workJobRepository;

    @Before
    public void setUp() throws Exception {
        workJobRepository = Mockito.mock(WorkJobRepository.class);
        when(workJobRepository.findById(any())).thenReturn(null);
        logger.info("check point test case setup");
    }

    @Test
    public void testNewIdDBWriter() throws Exception {
        logger.info("check point test case 1");
        // In here i am not asserting anything i just want the method call to succeed first
        readWorkJob.writeNewIdData(getRightData());
        logger.info(("check point test case 2"));
    }

    private JSONObject getRightData() throws IOException {
        String rightDataString = new String(Files.readAllBytes(Paths.get(RIGHT_DATA_FILE_NAME)));
        JSONObject jsonObject = new JSONObject(rightDataString);
        return jsonObject;
    }
}

but the mock is not working.

The test execution is not coming till the "check point qqqqq". I think that it's still trying to call the db.

Stack trace

java.lang.NullPointerException
16:42:31.387 [DEBUG] [TestEventLogger]         at 
com.shiv.work.job.ReadWorkJob.getExistingId(ReadWorkJob.java:102)
16:42:31.387 [DEBUG] [TestEventLogger]         at 
com.shiv.work.job.ReadWorkJob.writeNewIdDataTo(ReadWorkJob.java:71)
16:42:31.387 [DEBUG] [TestEventLogger] at com.shiv.work.job.ReadWorkJobTest.testNewIdDBWriter(ReadWorkJobTest.java:45)
16:42:31.387 [DEBUG] [TestEventLogger]         at 
java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
16:42:31.387 [DEBUG] [TestEventLogger]         at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
16:42:31.387 [DEBUG] [TestEventLogger]         at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
16:42:31.387 [DEBUG] [TestEventLogger]         at java.base/java.lang.reflect.Method.invoke(Method.java:567)
16:42:31.387 [DEBUG] [TestEventLogger]         at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
16:42:31.387 [DEBUG] [TestEventLogger]         at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
16:42:31.387 [DEBUG] [TestEventLogger]         at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
16:42:31.387 [DEBUG] [TestEventLogger]         at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
16:42:31.387 [DEBUG] [TestEventLogger]         at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
16:42:31.387 [DEBUG] [TestEventLogger]         at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
16:42:31.388 [DEBUG] [TestEventLogger]         at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
16:42:31.388 [DEBUG] [TestEventLogger]         at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
16:42:31.388 [DEBUG] [TestEventLogger]         at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
16:42:31.388 [DEBUG] [TestEventLogger]         at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
16:42:31.388 [DEBUG] [TestEventLogger]         at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
16:42:31.388 [DEBUG] [TestEventLogger]         at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
16:42:31.388 [DEBUG] [TestEventLogger]         at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
16:42:31.388 [DEBUG] [TestEventLogger]         at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
16:42:31.388 [DEBUG] [TestEventLogger]         at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:79)
16:42:31.388 [DEBUG] [TestEventLogger]         at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:85)
16:42:31.388 [DEBUG] [TestEventLogger]         at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
16:42:31.388 [DEBUG] [TestEventLogger]         at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
16:42:31.388 [DEBUG] [TestEventLogger]         at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
16:42:31.388 [DEBUG] [TestEventLogger]         at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
16:42:31.388 [DEBUG] [TestEventLogger]         at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
16:42:31.388 [DEBUG] [TestEventLogger]         at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
16:42:31.388 [DEBUG] [TestEventLogger]         at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
16:42:31.388 [DEBUG] [TestEventLogger]         at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
16:42:31.388 [DEBUG] [TestEventLogger]         at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
16:42:31.388 [DEBUG] [TestEventLogger]         at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
16:42:31.389 [DEBUG] [TestEventLogger]         at java.base/java.lang.reflect.Method.invoke(Method.java:567)
16:42:31.389 [DEBUG] [TestEventLogger]         at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
16:42:31.389 [DEBUG] [TestEventLogger]         at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
16:42:31.389 [DEBUG] [TestEventLogger]         at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
16:42:31.389 [DEBUG] [TestEventLogger]         at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
16:42:31.389 [DEBUG] [TestEventLogger]         at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
16:42:31.389 [DEBUG] [TestEventLogger]         at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:118)
16:42:31.389 [DEBUG] [TestEventLogger]         at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
16:42:31.389 [DEBUG] [TestEventLogger]         at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
16:42:31.389 [DEBUG] [TestEventLogger]         at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
16:42:31.389 [DEBUG] [TestEventLogger]         at java.base/java.lang.reflect.Method.invoke(Method.java:567)
16:42:31.389 [DEBUG] [TestEventLogger]         at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
16:42:31.389 [DEBUG] [TestEventLogger]         at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
16:42:31.389 [DEBUG] [TestEventLogger]         at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
16:42:31.389 [DEBUG] [TestEventLogger]         at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
16:42:31.389 [DEBUG] [TestEventLogger]         at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:412)
16:42:31.389 [DEBUG] [TestEventLogger]         at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
16:42:31.389 [DEBUG] [TestEventLogger]         at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
16:42:31.389 [DEBUG] [TestEventLogger]         at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
16:42:31.389 [DEBUG] [TestEventLogger]         at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
16:42:31.390 [DEBUG] [TestEventLogger]         at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
16:42:31.390 [DEBUG] [TestEventLogger]         at java.base/java.lang.Thread.run(Thread.java:830)

Upvotes: 1

Views: 1254

Answers (1)

Nkosi
Nkosi

Reputation: 247143

From the shown test code, the manually created repository is not being injected into the subject under test, which is why it is null and causing the NPE

Since using @Autowired you would also need to make sure the dependency is properly tagged to be mocked

@Mock
private WorkJobRepository workJobRepository;

@InjectMocks
private ReadWorkJob readWorkJob;

@Before
public void setUp() throws Exception {
    when(workJobRepository.findById(any())).thenReturn(null);
    logger.info("check point test case setup");
}

With that done the test should behave as expected.

When the dependency was created manually, it would not being injected into the subject under test.

Another approach would be to have the subject class follow explicit dependency principle

public class ReadWorkJob {

    private WorkJobRepository workJobRepository;

    @Autowired
    public ReadWorkJob (WorkJobRepository workJobRepository) {
        this.workJobRepository = workJobRepository;
    }

    public void writeNewIdData(JSONObject jsonObject) {
        WorkJob workJob = getExistingId(jsonObject.get("id").toString());
    }

    private WorkJob getExistingId(String id) {
        logger.info("check point ppppp");
        WorkJob workJob = workJobRepository.findById(id);
        logger.info("check point qqqqq");
        return workJob != null ? workJob : new WorkJob();
    }
}

And refactor the test accordingly

@RunWith(MockitoJUnitRunner.class)
public class ReadWorkJobTest {

    private static final String RIGHT_DATA_FILE_NAME = "test.txt";

    private ReadWorkJob readWorkJob;

    private WorkJobRepository workJobRepository;

    @Before
    public void setUp() throws Exception {
        workJobRepository = Mockito.mock(WorkJobRepository.class);
        when(workJobRepository.findById(any())).thenReturn(null);

        readWorkJob = new ReadWorkJob(workJobRepository); //<--

        logger.info("check point test case setup");
    }

    @Test
    public void testNewIdDBWriter() throws Exception {
        logger.info("check point test case 1");            
        readWorkJob.writeNewIdData(getRightData());
        logger.info(("check point test case 2"));
    }

    private JSONObject getRightData() throws IOException {
        String rightDataString = new String(Files.readAllBytes(Paths.get(RIGHT_DATA_FILE_NAME)));
        JSONObject jsonObject = new JSONObject(rightDataString);
        return jsonObject;
    }
}

Upvotes: 1

Related Questions