Search code examples
javaspringspring-bootjunit5

Spring Boot unit test constructor injection


I'm using Spring Boot to create a REST API and write some unit tests on my controllers. I know that the recommended manner to inject beans in spring is the constructor injection. But when i add the @SpringBootTest annotation to my test class, I can not inject my controller class with constructor, I find myself obliged to use @Autowired.

Have some explanation and is there another way to use constructor injection with SpringBootTest.

@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
class PersonControllerTest {

    @LocalServerPort
    private int port;

    @Autowired
    private PersonController controller;

    @Autowired
    private TestRestTemplate restTemplate;


    @Test
    public void greetingShouldReturnDefaultMessage() throws Exception {
        assertThat(this.restTemplate.getForObject("http://localhost:" + port + "/cvtech/Persons/",
                                                  String.class)).contains("content");
    }

    @Test
    public void contextLoads() throws Exception {
        assertThat(controller).isNotNull();
    }
    @Test
    void findAllByJob() {
    }
}

Solution

  • It's fine for your test to use field injection as the Test itself is not part of your domain; the test won't be part of your application context.

    Also

    You don't want to use SpringBootTest to test a controller, because that will wire ALL beans which can be way too heavy and time-consuming. Instead, you probably only want to create your controller and it's dependencies.

    So your best option is to use @WebMvcTest which will only create the beans required for testing the specified controller.

    @ExtendWith(SpringExtension.class)
    @WebMvcTest(controllers = PersonController.class)
    class PersonControllerTest {
        @Autowired
        private MockMvc mockMvc;
    
        @Test
        public void greetingShouldReturnDefaultMessage() throws Exception {
            mockMvc.perform(get("/cvtech/Persons"))
                   .andExpect(status().isOk())
                   .andExpect(content().string(contains("content")));
        }
    }
    

    Note that @WebMvcTest will search for a class annotated with @SpringBootConfiguration as it's default configuration. If it does not find it, or you want to manually specify some configuration classes, also annotate the test with @ContextConfiguration.

    Also, as a sidenote, when using TestRestTemplate, you don't need to specify host and port. Just call restTemplate.getForObject("/cvtech/persons", String.class)); Same when using MockMvc or WebTestClient.