java 解析已在 Spring 启动测试中使用的端口 DEFINED PORT

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42249791/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 06:29:09  来源:igfitidea点击:

Resolving port already in use in a Spring boot test DEFINED PORT

javaspring-bootintegration-testingspring-boot-test

提问by ahjashish

I have a spring boot application that fires up and executes a class that listens to Application Ready eventto call an external serviceto fetch some data and then use that data to push some rules to the classpath for execution. For local testing we have mocked the external servicewithin our application which works fine during the application startup.

我有一个 Spring Boot 应用程序,它启动并执行一个类,该类侦听Application Ready 事件以调用外部服务来获取一些数据,然后使用该数据将一些规则推送到类路径以供执行。对于本地测试,我们模拟了应用程序中的外部服务该服务在应用程序启动期间工作正常。

The issue is while testingthe application by running it with spring boot testannotation and embedded jetty container either on :

问题是在测试与运行它的应用程序春天开机测试注释和嵌入式码头集装箱无论是在:

  • RANDOM PORT
  • DEFINED PORT
  • 随机端口
  • 定义端口

In case of RANDOM PORT, at the application startup, it picks up the url for the mock service from the properties file at a defined port and has no clue where the embedded container is running since it is randomly picked up, hence failing to give response.

RANDOM PORT 的情况下,在应用程序启动时,它从定义端口的属性文件中获取模拟服务的 url,并且不知道嵌入式容器在哪里运行,因为它是随机获取的,因此无法给出响应.

In case of DEFINED PORT, for the first test case file it runs successfully, but the moment next file is picked up, it fails saying the port is already in use.

DEFINED PORT 的情况下,对于第一个测试用例文件,它成功运行,但是在拾取下一个文件的那一刻,它失败说该端口已在使用中。

The test cases are partitioned logically in multiple files and need the external service to be called before the container starts to load the rules.

测试用例在多个文件中逻辑分区,需要在容器开始加载规则之前调用外部服务。

How can I either share the embedded container between test files in case of using defined port or refactor my application code instead to get hold of the random port while starting up during the test case execution.

在使用定义的端口的情况下,如何在测试文件之间共享嵌入式容器,或者重构我的应用程序代码以在测试用例执行期间启动时获取随机端口。

Any help would be appreciated.

任何帮助,将不胜感激。

Application Startup code :

应用程序启动代码:

@Component
public class ApplicationStartup implements ApplicationListener<ApplicationReadyEvent> {

@Autowired
private SomeService someService;

@Override
public void onApplicationEvent(ApplicationReadyEvent arg0) {

    try {
        someService.callExternalServiceAndLoadData();
    }
    catch (Execption e) {}
    }
 }

Test Code Annotations: Test1

测试代码注释:Test1

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@TestPropertySource("classpath:test-application.properties")
public class Test1 {

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    public void tc1() throws IOException {.....}

Test Code Annotations: Test2

测试代码注释:Test2

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@TestPropertySource("classpath:test-application.properties")
public class Test2 {

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    public void tc1() throws IOException {.....}

回答by Joel Neukom

If you insist on using the same port on multiple test, you can prevent spring from caching the context for further tests by annotating your testclass with: @DirtiesContext

如果您坚持在多个测试中使用相同的端口,您可以通过使用以下内容注释您的测试类来防止 spring 缓存上下文以进行进一步的测试:@DirtiesContext

In your case:

在你的情况下:

@RunWith(SpringRunner.class)
@DirtiesContext
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@TestPropertySource("classpath:test-application.properties")

Here is a quote from Andy Wilkinson from his answer on this discussion

这是安迪·威尔金森 (Andy Wilkinson) 对本次讨论的回答中的引述

This is working as designed. Spring Framework's test framework will, by default, cache contexts for possible reuse by multiple test classes. You have two tests with different configuration (due to @TestPropertySource) so they will use different application contexts. The context for the first test will be cached and kept open while the second test is running. Both tests are configured to use the same port for Tomcat's connector. As a result, when the second test is run, the context fails to start due to a port clash with the connector from the first test. You have a few options:

  1. Use RANDOM_PORT
  2. Remove @TestPropertySource from Test2 so that the contexts have identical configuration and the context from the first test can be reused for the second test.
  3. Use @DirtiesContext so that the context isn't cached

这是按设计工作的。Spring Framework 的测试框架默认会缓存上下文以供多个测试类可能重用。您有两个具有不同配置的测试(由于@TestPropertySource),因此它们将使用不同的应用程序上下文。第一个测试的上下文将被缓存并在第二个测试运行时保持打开状态。两个测试都配置为对 Tomcat 的连接器使用相同的端口。结果,当运行第二个测试时,由于端口与第一个测试中的连接器发生冲突,上下文无法启动。您有几个选择:

  1. 使用 RANDOM_PORT
  2. 从 Test2 中删除 @TestPropertySource 以便上下文具有相同的配置,并且第一个测试中的上下文可以重用于第二个测试。
  3. 使用 @DirtiesContext 以便上下文不被缓存

回答by cbreezier

I ran across the same issue. I know this question is a little old, but this may be of assistance:

我遇到了同样的问题。我知道这个问题有点老,但这可能有帮助:

Tests that use @SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT) can also inject the actual port into a field by using the @LocalServerPort annotation, as shown in the following example:

使用@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT) 的测试也可以使用@LocalServerPort 注解将实际端口注入到字段中,如下例所示:

Source: https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-user-a-random-unassigned-http-port

来源:https: //docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#howto-user-a-random-unassigned-http-port

The code example given is:

给出的代码示例是:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
public class MyWebIntegrationTests {

    @Autowired
    ServletWebServerApplicationContext server;

    @LocalServerPort
    int port;

    // ...

}