0

I am working on a Spring Java-based project. For testing purposes I have implemented a H2 in-memory DB, which has been filled with data via an xml doc. I whish to test a method which updates database records via an incoming datagram. First of all, this method check if there are any relevant data in the datagram arrived which are found in the database(here is a repository call), then calls for another external service with the given ids, which provides the actual data which needs to be placed into the DB instead of the already saved ones. So, there is a repository call first, then a second call for an external service. When I wish to test this function, I am using following annotations:

@RunWith(SpringRunner.class)
@SpringBootTest
@DatabaseSetup("classpath:datasets/FileWhichContainsDbRecords.xml")

Then I mock the external service with:

@MockBean
private ServiceName myService;

And call the Spring repository with:

@Autowired
private RepositoryName repositoryName;

And have the following test code:

@Test
@Transactional
public void testCase() {
    GivenDg dg = Factory.createDg("id1", Collections.singletonList(Factory.createAnotherObject("id2", OPERATION_TYPE_MODIFY, Collections.singletonList("channel"))));

    MyObject myObjectSavedInDB = repositoryName.findByProvidedIds("id1", "id2").get();

    when(serviceName.getData(anyString(), anyString(), anyString(), any())).thenReturn(
            Factory.createActualData(false, false, false, "HUF", Collections.singletonList("channels")));

    myService.updateFunction(dg);

    Optional<MyObject> optResult = repositoryName.findByProvidedIds("id1", "id2");
    assertTrue(optResult.isPresent());
    assertNotSame(optResult.get().isA(), myObjectSavedInDB.isA());
}

So, I which to mock the ServiceName service, and I have provided the response which I wish to have when this function is called but I keep getting NullPointer exception due to ServiceName is null. It does not even get mocked.

Is it not possible to test repository and mock a service in the same time? What am I doing wrong?

I would really appreciate any help!

Thank you!


enter image description here


java.lang.NullPointerException
at ---------)
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.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
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.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
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)

Ok, I found what was missing... Originally, I had the following annotations on my Test class:

@RunWith(SpringRunner.class)
@SpringBootTest()
@DatabaseSetup("classpath:datasets/FileWhichContainsDbRecords.xml.xml")
@ContextConfiguration(classes = {TestRepositoryConfig.class})
@TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
    DirtiesContextTestExecutionListener.class,
    TransactionalTestExecutionListener.class,
    DbUnitTestExecutionListener.class})
@DirtiesContext()
@Transactional

I had to add MockitoTestExecutionListener.class to the list of TestExecutionListeners....

dorcsi
  • 295
  • 2
  • 6
  • 24

1 Answers1

1

There's something missing in the code you posted. I would expect to see:

@RunWith(SpringRunner.class)
@SpringBootTest
@DatabaseSetup("classpath:datasets/FileWhichContainsDbRecords.xml")
class MyTest {
    @Autowired private WidgetRepository repository;
    @Autowired private ServiceUnderTest service;
    @MockBean private ExternalService externalService;

    @Test
    @Transactional
    public void testWidgetService() {
        // ...
        when(externalService.someCall()).thenReturn(Factory.whatever());
        // ...
    }
}

In other words, I would expect to see you @Autowire both the service you're testing and the repository, and @MockBean only the external service. And then setting mock behaviour on the external service, which is mocked. But you seem to be mocking the service under test, autowiring the repository only, and then setting mock behaviour on the external service; but you don't show us how you're setting up the external service - maybe it's not set up at all and that's why it's null.

I think it would help you to reduce your test class to something small enough to post here in its entirety. You might well find the explanation while you're doing so.

Andrew Spencer
  • 15,164
  • 4
  • 29
  • 48