简体   繁体   English

Spring Boot 单元测试构造函数注入

[英]Spring Boot unit test constructor injection

I'm using Spring Boot to create a REST API and write some unit tests on my controllers.我正在使用 Spring Boot 创建一个 REST API 并在我的控制器上编写一些单元测试。 I know that the recommended manner to inject beans in spring is the constructor injection.我知道在spring中注入bean的推荐方式是构造函数注入。 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 .但是当我将@SpringBootTest注释添加到我的测试类时,我无法用构造函数注入我的控制器类,我发现自己不得不使用@Autowired

Have some explanation and is there another way to use constructor injection with SpringBootTest .有一些解释,还有另一种方法可以将构造函数注入与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() {
    }
}

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.您不想使用SpringBootTest来测试控制器,因为这将连接所有可能过于繁重和耗时的 bean。 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.所以你最好的选择是使用@WebMvcTest ,它只会创建测试指定控制器所需的bean。

@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.请注意, @WebMvcTest将搜索带有@SpringBootConfiguration注释的类,因为它是默认配置。 If it does not find it, or you want to manually specify some configuration classes, also annotate the test with @ContextConfiguration .如果它没有找到它,或者您想手动指定一些配置类,也可以使用@ContextConfiguration注解测试。

Also, as a sidenote, when using TestRestTemplate , you don't need to specify host and port.此外,作为旁注,使用TestRestTemplate时,您不需要指定主机和端口。 Just call restTemplate.getForObject("/cvtech/persons", String.class));只需调用restTemplate.getForObject("/cvtech/persons", String.class)); Same when using MockMvc or WebTestClient .使用MockMvcWebTestClient时相同。

For those using Kotlin, using field-injection means having to use lateinit var fields.对于那些使用 Kotlin 的人来说,使用字段注入意味着必须使用lateinit var字段。 Which is far from ideal.这远非理想。

It is possible to use constructor injection on SpringBoot tests however, using the @TestConstructor :但是,可以在 SpringBoot 测试中使用构造函数注入,使用@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
...
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM