4

There is a @Value annotated constant, which is not getting initialized when running test, it throws NullPointerException when it is required inside constructor.

Sample Class to be tested:

class TestClass {
    @Value("${test.value1}")
    private String value1;

    private final TestTemplate testTemplate;

    public TestClass(TestTemplateBuilder builder) {
        testTemplate = builder.someMethod(value1).build();
    }


    ---
}

Sample Test Class:

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = TestClass.class)
@SpringBootTest
class TestClassTest {

    @MockBean
    TestTemplateBuilder builder;

    @Autowired
    TestClass testClass = new TestClass(testTemplate);

    @Before
    public void setUp() {
        ReflectionTestUtils.setField(testClass, "value1", "VALUE");

        Mockito.when(builder.build()).thenReturn(new TestTemplate());
    }

    ---
}

Things tried, but nothing worked:

  • I have created application.properties file with required value.
  • Created application-test.properties and added @TestPropertySource(locations="classpath:application-test.properties").
  • @SpringBootTest(properties = { "test.value1=VALUE" })

I have tried some other things also, but what i got is NullPoiterException at someMethod(value1).

Versions:

  • Java: 1.8
  • Springboot: 2.1.17
  • Junit: 4.12
  • Mockito: 2.23.4
  • What can I say… ditch Mockito and use Spock instead, then add the @SpringBootTest annotation and you get a full testing container for free. – Bohemian Jan 06 '22 at 06:59
  • 1
    You are creating an aninstance of `TestClass` yourself. It has nothing injected or processed. Remove the `new TestClass` to init the field. Also you should register behavior but you are already calling the mock in the constructor so this will be impossible. What you should do is don't use Spring for this test, create the mock and instances yourself and run the tests. You don't have to use Spring for everything especially if you want to write a simple unit test. – M. Deinum Jan 06 '22 at 07:12
  • 1
    @M.Deinum i got the point of removing unnecessary things. Could you share a sample or some link which help me understand. – towardinfinity Jan 23 '22 at 07:24

4 Answers4

2

If @Before is not working, then try with @BeforeEach. It worked for me.

`@BeforeEach
public void setUp() {
    ReflectionTestUtils.setField(yourInstanceClass, "fieldName", "fieldValue");
}`
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jun 23 '22 at 09:34
1

Just to make sure (because in the question, it's doesn't showed): class TestClass is Spring bean (Component, Service...)?

Edit (TestClass is Component):

This is not how Spring bean are created;

If constructor of TestClass require @Value then only Spring will guarantee that before instantiating TestClass the test.value1 value is available. So you can add to the constructor @Value("${test.value1}")String value1 and set the value1 property.

Please read the ways of how Spring inject.

Shay Zambrovski
  • 401
  • 5
  • 21
0

The reason you are getting NullPointerException is that the value injection occurs after the constructor call. To prevent this you can use spring life cycle hooks to initialize your class after the constructor call.

Tarun
  • 121
  • 7
  • 1
    `value1` is used inside constructor so it's value should have been initialized before constructor call. As you say **value injection occurs after the constructor call** can you back this by some documentation. – towardinfinity Jan 23 '22 at 07:20
0

I was not able to solve the main issue, but i fulfilled my requirement like this.

class TestClassTest {

    TestClass testClass;

    @Before
    public void setUp() {
        TestTemplateBuilder builder = Mockito.mock(TestTemplateBuilder.clsss);

        TestTemplateBuilder builder1 = Mockito.mock(TestTemplateBuilder.clsss);

        Mockito.when(builder.someMethod(nullable(String.class))).thenReturn(builder1);

        Mockito.when(builder1.build()).thenReturn(new TestTemplate());

        testClass = new TestClass(builder);
    }

    ---
}

This might not be the correct way of doing.