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 :
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 {.....}
Usually, the most straightforward way to configure the HTTP port of a Spring Boot application is by defining the port in the configuration file application. properties or application. yml. Next, let's go through the two scenarios and discuss different ways to get the port programmatically at runtime.
1) On the top right corner of your console, there is a red button, to stop the spring boot application which is already running on this port just click on the red button to terminate. 2) If the red button is not activated you need to right click on the console and select terminate/disconnect all. Hope this helps.
You can get this information via Environment for the port and the host you can obtain by using InternetAddress . Getting the port this way will only work, if a) the port is actually configured explicitly, and b) it is not set to 0 (meaning the servlet container will choose a random port on startup).
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:
- Use RANDOM_PORT
- 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.
- Use @DirtiesContext so that the context isn't cached
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;
// ...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With