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

25,818

Solution 1

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

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

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

Solution 2

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:

Source: 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;

    // ...

}
Share:
25,818
ahjashish
Author by

ahjashish

Updated on July 09, 2022

Comments

  • ahjashish
    ahjashish almost 2 years

    I have a spring boot application that fires up and executes a class that listens to Application Ready event to call an external service to 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 service within our application which works fine during the application startup.

    The issue is while testing the application by running it with spring boot test annotation 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.

    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.

    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

    @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

    @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 {.....}