【问题标题】:Difference between using MockMvc with SpringBootTest and Using WebMvcTest使用带有 SpringBootTest 的 MockMvc 和使用 WebMvcTest 之间的区别
【发布时间】:2017-02-13 09:42:27
【问题描述】:

我是 Spring Boot 新手,正在尝试了解 SpringBoot 中的测试工作原理。我对以下两个代码sn-ps有什么区别感到有些困惑:

代码 sn-p 1:

@RunWith(SpringRunner.class)
@WebMvcTest(HelloController.class)
public class HelloControllerApplicationTest {
    @Autowired    
    private MockMvc mvc;

    @Test
    public void getHello() throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().string(equalTo("Greetings from Spring Boot!")));
    }
}

此测试使用@WebMvcTest 注释,我认为它是用于功能切片测试并且仅测试 Web 应用程序的 MVC 层。

代码 sn-p 2:

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
public class HelloControllerTest {

    @Autowired
    private MockMvc mvc;

    @Test
    public void getHello() throws Exception {
    mvc.perform(MockMvcRequestBuilders.get("/").accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk())
            .andExpect(content().string(equalTo("Greetings from Spring Boot!")));
    }
}

此测试使用@SpringBootTest 注释和MockMvc。那么这与代码 sn-p 1 有什么不同呢?这有什么不同?

编辑: 添加代码片段 3(在 Spring 文档中找到这个作为集成测试的示例)

@RunWith(SpringRunner.class) 
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 
public class HelloControllerIT {
    
    @LocalServerPort private int port;
    private URL base;
    
    @Autowired private TestRestTemplate template;
    
    @Before public void setUp() throws Exception {
        this.base = new URL("http://localhost:" + port + "/");
    }
    
    @Test public void getHello() throws Exception {
        ResponseEntity < String > response = template.getForEntity(base.toString(), String.class);
        assertThat(response.getBody(), equalTo("Greetings from Spring Boot!"));
    }
}

【问题讨论】:

    标签: spring-boot


    【解决方案1】:

    @SpringBootTest 是通用的测试注解。如果您正在寻找在 1.4 之前做同样事情的东西,那就是您应该使用的东西。它根本不使用 slicing,这意味着它将启动您的完整应用程序上下文并且根本不自定义组件扫描。

    @WebMvcTest 只会扫描您定义的控制器和 MVC 基础结构。而已。因此,如果您的控制器对您的服务层中的其他 bean 有某种依赖性,则测试将不会开始,直到您自己加载该配置或为其提供模拟。这要快得多,因为我们只加载您应用程序的一小部分。此注解使用切片。

    Reading the doc 应该也能帮到你。

    【讨论】:

    • 非常感谢您的回复!!。因此,如果我理解正确,这意味着代码 sn-ps 都只测试应用程序的 MVC 部分。但是 tcode sn-p 1 加载完整的应用程序上下文,而 code sn-p 2 只扫描控制器。它是否正确?代码 sn -p 1 是否可以作为单元测试来测试控制器?
    • 不,这是不正确的。 SpringBootTest 正在加载您的完整应用程序(在某种程度上,默认情况下,如果有可用的嵌入式容器,它不会启动嵌入式容器,这就是 webEnvironment 的用途)。我不会说@SpringBootTest 是控制器的单元测试,但更多的是集成测试,真的。 WebMvcTest 实际上是你的控制器的单元测试,如果它有依赖关系,你必须自己提供它们(配置或某种模拟)。
    • 再次感谢您的回复。我已经编辑了问题并添加了代码 sn-p 3。您提到 @SpringBootTest 注释更多地用于集成测试。我相信 Snippet 3 证明了这一点。因此,如果集成测试像在 Snippet 3 中那样进行,那么 Snippet 2 会做什么? Snippet 2 使用 SpringBootTest 注解和模拟环境(wenEnvironment 属性的默认值)。此外,sn-p 3 启动嵌入式服务器并进行真正的 HTTP 调用,而 sn-p 2 不这样做。那么考虑到这一点,sn-p 2 不能算是单元测试吗?
    • 我不确定我们是否会在这里解决这个问题。也许是吉特?您似乎经常错过的是SpringBootTestWebMvcTest 创建的应用程序上下文大不相同。前者加载您的整个应用程序并启用所有自动配置,而后者仅启用 Spring Mvc 并且除了HelloController 之外不扫描任何内容。毕竟,这完全取决于您所说的单元测试是什么意思。但这就是区别。
    • 感谢您的回复。这对我很有帮助。现在我明白了为什么我的测试可以使用 SpringBootTest 运行,但使用 WebMvcTest 时会出现异常。再次非常感谢。
    【解决方案2】:

    @SpringBootTest 注释告诉 Spring Boot 去寻找一个主配置类(例如一个带有 @SpringBootApplication 的配置类),并使用它来启动一个 Spring 应用程序上下文。 SpringBootTest 加载完整的应用程序并注入所有可能很慢的 bean。

    @WebMvcTest - 用于测试控制器层,您需要提供使用 Mock 对象所需的剩余依赖项。

    下面还有一些注释供您参考。

    测试应用程序的切片 有时您想测试应用程序的一个简单“切片”,而不是自动配置整个应用程序。 Spring Boot 1.4 引入了 4 个新的测试注解:

    @WebMvcTest - for testing the controller layer
    @JsonTest - for testing the JSON marshalling and unmarshalling
    @DataJpaTest - for testing the repository layer
    @RestClientTests - for testing REST clients
    

    更多信息请参考:https://spring.io/guides/gs/testing-web/

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-08
    • 2013-10-03
    • 1970-01-01
    • 1970-01-01
    • 2014-09-20
    • 2011-07-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多