0

I'm attempting to perform a junit test on my controller which receives a variable from application.yml file and prints it out.

Properties class in src/main/java:

@Data
@Validated
@ConfigurationProperties(EchoProperties.PREFIX)
@Component
public class EchoProperties {

    public static final String PREFIX = "vmh0.echo";

    private String message;

}

Controller class that autowires the properties class also in src/main/java:

@RestController
@RequestMapping("/api/echo-echoplusplus")
public class EchoPlusPlusController {
    
    @Autowired
    EchoProperties echoProperties;

    @GetMapping("/echomessage")
    public EchoPlusPlusResponse echoMessage() {
        return new EchoPlusPlusResponse(new EchoPlusPlusRes(echoProperties.getMessage()));
    }
}

I have an application.yml in my src/main/resources and application-test.yml in my src/test/resources:

vmh0:
 echo:
  message: "hello world"

Test class in src/test/java:

@EnableConfigurationProperties
@ContextConfiguration(classes = EchoProperties.class)
class EchoPlusPlusControllerTest {

    @InjectMocks
    EchoPlusPlusController echoPlusPlusController;

    @Autowired
    EchoProperties echoProperties;

    private MockMvc mockMvc;

    @BeforeEach()
    void setUp() throws Exception {
        MockitoAnnotations.openMocks(this);
        mockMvc = MockMvcBuilders.standaloneSetup(echoPlusPlusController).build();
        assertNotNull(mockMvc);
        assertNotNull(echoPlusPlusController);
    }

    @Test
    final void testEchoMessageSuccess() throws Exception {
        try {
            MockHttpServletResponse mockGetMessageResponse = mockMvc
                    .perform(MockMvcRequestBuilders.get("/api/echo-echoplusplus/echomessage")
                            .contentType(MediaType.APPLICATION_JSON))
                    .andExpect(status().isOk()).andReturn().getResponse();

            String responseString = mockGetMessageResponse.getContentAsString();
            EchoPlusPlusResponse actualResponse = new ObjectMapper().readValue(responseString,
                    EchoPlusPlusResponse.class);
            
            System.out.println("greeting: " + echoProperties.getMessage());

            assertNotNull(mockGetMessageResponse);
            assertEquals("hello world!", actualResponse.getEchoPlusPlusRes().getMessage());


        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

Error:

java.lang.NullPointerException: Cannot invoke "com.config.properties.EchoProperties.getMessage()" because "this.echoProperties" is null
    at com.controller.EchoPlusPlusControllerTest.testReadProperty(EchoPlusPlusControllerTest.java:42)

The project is functionally correct when it runs, but can't figure out how to have my EchoProperties class read from application-test.yml during testing.

I'm using JUnit 5 and Springboot 3

tpdovu
  • 1
  • 1
  • Did you set up this variable for application.yml in test folder? – Feel free May 05 '23 at 18:31
  • @Feelfree yes, I have an application-test.yml in my test/resources. I also tried with application.yml in test/resources and both have not worked – tpdovu May 05 '23 at 19:06

1 Answers1

0

You're not mocking the nested service, that's why it doesn't work. Using @InjectMocks you are not mocking EchoProeprties.class.

@InjectMocks annotation tells to Mockito to inject all mocks (objects annotated by @Mock annotation)

Here is code example:

@ExtendWith({SpringExtension.class})
@ContextConfiguration(classes = {EchoPlusPlusController.class, EchoProperties.class})
@ActiveProfiles("test")
class EchoPlusPlusControllerTest {

    @Autowired
    EchoPlusPlusController echoPlusPlusController;

    @Autowired
    EchoProperties echoProperties;

    private MockMvc mockMvc;

    @BeforeEach()
    void setUp() throws Exception {
        MockitoAnnotations.openMocks(this);
        mockMvc = MockMvcBuilders.standaloneSetup(echoPlusPlusController).build();
        assertNotNull(mockMvc);
        assertNotNull(echoPlusPlusController);
    }

    @Test
    final void testEchoMessageSuccess() throws Exception {
        try {
            MockHttpServletResponse mockGetMessageResponse = mockMvc
                    .perform(MockMvcRequestBuilders.get("/api/echo-echoplusplus/echomessage")
                            .contentType(MediaType.APPLICATION_JSON))
                    .andExpect(status().isOk()).andReturn().getResponse();

            String responseString = mockGetMessageResponse.getContentAsString();
            EchoPlusPlusResponse actualResponse = new ObjectMapper().readValue(responseString,
                    EchoPlusPlusResponse.class);

            assertNotNull(mockGetMessageResponse);
            assertEquals("hello world!", actualResponse.getMessage());


        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}
KSs
  • 420
  • 3
  • 9
  • I forgot to mention that I did try to @Mock EchoProperties. Based on this post https://stackoverflow.com/questions/58995002/how-to-load-values-from-custom-properties-file-for-junit-testing-using-mockito, mocked objects are not able to read values from properties files. This is what led me to autowiring EchoProperties. In your answer I see you did use Autowired for both the dependent objects. I tried that and received the same error. – tpdovu May 09 '23 at 16:49