webEnvironment = RANDOM_PORT和webEnvironment = MOCK的区别

17

我编写了Spring Boot集成测试,并且它正在工作。这是测试配置:

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = RANDOM_PORT)
@AutoConfigureMockMvc
@Transactional
public class SomeTest {
   @Autowired
   private MockMvc mvc;

   @Test
   public void insertEventTest(){
      ...testing something...
   }

}

我知道在设置webEnvironment = RANDOM_PORT时,Spring将初始化一个嵌入式Web服务器并对其运行此测试。运行此测试时,我查看日志并发现嵌入的TomcatWebServer已启动。初始化Tomcat大约需要6秒钟,但是在日志的这两个部分之间,还初始化了一些其他bean,因此我非常确定初始化Tomcat并不是6秒钟,而是少于6秒钟。

2019-10-13 16:03:20.065  INFO 8596 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 0 (http)
2019-10-13 16:03:20.098  INFO 8596 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2019-10-13 16:03:20.098  INFO 8596 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.14]
2019-10-13 16:03:20.108  INFO 8596 --- [           main] o.a.catalina.core.AprLifecycleListener   : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/usr/java/packages/lib:/usr/lib64:/lib64:/lib:/usr/lib]
2019-10-13 16:03:20.228  INFO 8596 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext

...还有一些日志,最后

  2019-10-13 16:03:26.366  INFO 8596 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 38335 (http) with context path ''

我运行了3次测试,完成测试所需的时间分别是12秒、11.4秒和12秒。之后,我尝试设置 @SpringBootTest(webEnvironment = MOCK) 。我发现这一次并没有初始化Tomcat(Web服务器被spring模拟),执行时间为11.3秒、11秒和10.8秒。在两种情况下,所有测试均通过。我的想法是,使用模拟的Web服务器可以提高测试性能,但最后只得到了1秒的收益。如果我们考虑到我的应用程序上下文在测试类之间被缓存,那么我基本上什么也没得到。所以我的问题是,在哪些情况下使用RANDOM_PORT测试会通过而MOCK测试失败,反之亦然?我应该何时使用RANDOM_PORTMOCK

3个回答

12

使用@SpringBootTest(webEnvironment = WebEnvironment.MOCK)会加载web应用程序上下文并提供模拟环境。它不会加载真正的http服务器,只是模拟整个网络服务器的行为。

WebEnvironment.MOCK带来了一些优势,例如易于使用或隔离其他因素,但可能不是很好的集成测试实践。

集成测试应尽可能接近生产环境。考虑到这一点,使用@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)将是更好的选择。这种方法更接近于测试真正的应用程序。您可以看到整个系统是否会像预期的那样工作。

当您使用@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)时,您将使用真正的http服务器进行测试。在这种情况下,您需要使用TestRestTemplate。当您想要测试与Web层相关的某些周围行为时,这非常有用。

TestRestTemplate是Spring RestTemplate的便利替代品,在集成测试中非常有用。……如果您使用带有WebEnvironment.RANDOM_PORT或WebEnvironment.DEFINED_PORT注释的@SpringBootTest注释,则可以注入完全配置的TestRestTemplate......

这是一个关于Spring Boot测试工具的链接。该页面提供了有关如何使用RestTemplate进行集成测试的信息。请参考链接:https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-rest-templates-test-utility

你能举出一些例子,说明何时应该使用其中一种方法而不是另一种方法,例如何时一种测试会通过而另一种测试会失败吗?顺便说一下,我的测试即使在mockmvc和RANDOM_PORT环境下也可以工作,因此在使用RANDOM_PORT时我不必使用testresttemplate。 - Spasoje Petronijević
1
当然,你不必强制使用TestRestTemplate,这只是一个建议。为了测试真实对象的集成,例如测试客户端和服务器交互,应该使用RANDOM_PORT。正如我所说,集成测试应尽可能与生产环境相似,使用MOCK在这里没有意义。否则,你的系统很可能会在生产中失败。你应该在某些情况下使用MOCK,比如测试控制器逻辑而不需要运行Web服务器。希望这能帮到你。 - Hülya
仍不清楚在哪些情况下仅使用 RANDOM_PORT 将捕获故障,而 MOCK 不会。 - Morteza
我正在运行一个webEnvironment = MOCK的Spring Boot测试,但是出现了错误org.springframework.boot.web.server.PortInUseException: Port 8080 is already in use。如果你说“它不会加载真正的HTTP服务器”,那怎么可能呢?从堆栈跟踪中可以看出,它正在加载(并尝试启动)Tomcat。 - Frans
使用 RANDOM_PORT 时,您不必使用 TestRestTemplate。虽然这样更简单,但您也可以通过使用 @LocalServerPort@Value("${local.server.port}") 让 Spring 从环境中注入端口。 - Nico Van Belle

1
  1. WebEnvironment.DEFINED_PORT - 在应用程序固定属性文件中表示嵌入式Web服务器的DEFINED_PORT。您可以使用RestTemplate或TestRestTemplate来测试应用程序。创建一个(反应式)Web应用程序上下文。
                application-fixed.properties 
                server.port=7777
            
            @RunWith(SpringRunner.class)
            @SpringBootTest(classes = Application.class,
              webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
            @ActiveProfiles("fixed")
            public class ApplicationTest {
                private final static int EXPECTED_PORT = 7777;
                ....
            }
  1. WebEnvironment.RANDOM_PORT - 不想为测试Spring Boot指定任何端口。您可以使用RestTemplate或TestRestTemplate来测试应用程序。创建一个Web应用程序上下文(响应式或基于Servlet的)。
                application-random.properties 
                server.port=0 - ServletWebServerApplicationContext.getWebServer().getPort() to get embedded server port. 
  1. WebEnvironment.MOCK - @SpringBootTest没有参数或者参数为WebEnvironment.MOCK时,不会加载真正的HTTP服务器。您不能使用RestTemplate或TestRestTemplate测试应用程序,必须使用@MockMVC。

如果类路径中存在servlet API,则创建一个带有模拟servlet环境的WebApplicationContext。


1
有些情况下,MOCK无法捕获失败,而RANDOM_PORT可以,因为它启动了一个真正的Web服务器。
示例在docs中:
例如,Spring Boot的错误处理基于Servlet容器提供的“错误页面”支持。这意味着,虽然您可以测试MVC层是否按预期引发和处理异常,但无法直接测试特定的自定义错误页面是否呈现。如果您需要测试这些更低级别的问题,则可以像下一节所述那样启动一个完全运行的服务器。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接