2

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() { } } 
2
  • You don't want Spring and DI for Unit tests. Use it for Integration tests. If it's an integration test, I advice you look into MockMVC Commented Oct 30, 2020 at 9:16
  • Thank you, but i think that SpringBootTest do both o them. Commented Oct 30, 2020 at 9:26

2 Answers 2

3

For those using Kotlin, using field-injection means having to use lateinit var fields. Which is far from ideal.

It is possible to use constructor injection on SpringBoot tests however, using the @TestConstructor:

@ExtendWith(SpringExtension::class) @TestConstructor(autowireMode = ALL) @SpringBootTest( classes = [MyApplication::class], webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, ) internal class MyIntegrationTest( val beanA: BeanA, @Qualifier("some qualified") val beanB: BeanB, ) { ... // Tests can use the beans injected in the constructor without any problems ... } 
Sign up to request clarification or add additional context in comments.

Comments

2

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.