1

I am new to Mockito and i am facing a issue due to a property is not loading in from appication.properties file.

Problem statement: I am trying to mock a method which uses a property from the application.properties file. When the control arrives at the line to load property value it shows null and because of this mockito throws java.lang.NullPointerException.

What i am looking for is how to load the property from application.properties file when mocking a method. Here i am trying to load global variable partsListGlobal .Please help me how to achieve this.?

Here is my below code snippet.

@Service
public class ClimoDiagnosticReportServImpl implements ClimoDiagnosticReportService {

    @Value("${PARTS_LIST}")
    private String partsListGlobal;

    @Override
    public boolean getSomeResult() {
        String[] partsListLocal = getPartsList();

        List<String> partsList = Arrays.asList(partsListGlobal);

        if (partsList.contains("PART_X1"))
            return true;
        else
            return false;
    }

    public String[] getPartsList() {
        return partsListGlobal.split(",");// Here is the error occuring due to partsListGlobal is not loading the value from application.properties file.
    }
}

@RunWith(MockitoJUnitRunner.class)
public class ClimoDiagnosticReportServImplTest {

    @InjectMocks
    private ClimoDiagnosticReportServImpl serviceReference1;

    @Mock
    private ClimoDiagnosticReportServImpl serviceReference12;

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

    @Test
    public void getSomeResultTest() {

        boolean result1 = false;
        String[] strArray = new String[2];
        strArray[0] = "P1";
        strArray[1] = "P2";
        Mockito.when(serviceReference12.getPartsList()).thenReturn(strArray);
        boolean result2 = serviceReference1.getSomeResult();
        Assert.assertEquals(result1,result2);

    }
}

Error:

java.lang.NullPointerException at com.test.serviceimpl.ClimoDiagnosticReportServImpl.getPartsList(ClimoDiagnosticReportServImpl.java:68) at com.test.serviceimpl.ClimoDiagnosticReportServImpl.getSomeResult(ClimoDiagnosticReportServImpl.java:57) at com.test.serviceimpl.ClimoDiagnosticReportServImplTest.getSomeResultTest(ClimoDiagnosticReportServImplTest.java:74) 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.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

Thanks everyone in advance.

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
  • Possible duplicate of [How do I mock an autowired @Value field in Spring with Mockito?](https://stackoverflow.com/questions/23162777/how-do-i-mock-an-autowired-value-field-in-spring-with-mockito) – Fullstack Guy Dec 29 '18 at 14:00
  • Use constructor injection instead of field injection, and pass whatever String value you want to the constructor in your unit test. – JB Nizet Dec 29 '18 at 14:02
  • Why do you have two `ClimoDiagnosticReportServImpl` ? `serviceReference1` is the Service you want to test, but what about `serviceReference12` ? `@Mock` is to mock the dependencies (`@Autowired` fields) of your service, but you don't have any dependency. – Ricola Dec 29 '18 at 14:03
  • @Ricola do you mean say that no need of using serviceReference12 ..? rather use only serviceReference1..? – user3884306 Dec 30 '18 at 11:12
  • My question is rather : what do you want to do with `serviceReference12`? – Ricola Dec 30 '18 at 11:15

1 Answers1

0

You don't have any dependency to mock in the service. Mockito is thus completely unnecessary. What you need to do is to set the private String field, that is populated by Spring in your application using reflection.

Just follow the best practice os using cnstructor injection instead of field injection, and that will make your code testable (that's one of the reasons why it's a best practice):

@Service 
public class ClimoDiagnosticReportServImpl implements ClimoDiagnosticReportService {

    private String partsListGlobal;

    public ClimoDiagnosticReportServImpl(@Value("${PARTS_LIST}") String partsListGlobal) {
        this.partsListGlobal = partsListGlobal;
    }

    // ...
}

Your test now can be reduced to

public class ClimoDiagnosticReportServImplTest {

    @Test
    public void shouldReturnTrueIfPropertyContainsPartX1() {
        ClimoDiagnosticReportServImpl service = new ClimoDiagnosticReportServImpl("a,b,c,PART_X1,d");
        assertTrue(service.getSomeResult());
    }

    @Test
    public void shouldReturnFalseIfPropertyDoesNotContainPartX1() {
        ClimoDiagnosticReportServImpl service = new ClimoDiagnosticReportServImpl("a,b,c,d");
        assertFalse(service.getSomeResult());
    }
}
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Thank you for the precious input.But i have a small concern, what if i have too many properties to be loaded from application.properties file..? Should istill follow the same constructor injection ..? – user3884306 Dec 29 '18 at 14:55
  • If a bean depends on so many properties, it probably means that it has too many responsibilities, and should be split into several beans. In general, if you have multiple properties regarding a given functionality, you should use a POJO grouping all those properties and annotated with ConfigurationProperties. And inject that POJO to your bean. https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config-typesafe-configuration-properties – JB Nizet Dec 29 '18 at 15:00