I'm using spring-boot
with WebClient
, which is autowired as a bean.
Problem: when writing a junit
integration test, I have to use okhttp MockWebServer
. This mock always starts up on a random port, eg localhost:14321
.
Now my WebClient
of course has a fixed url that it sends the requests to. This url may be given by an application.properties
parameter like webclient.url=https://my.domain.com/
, so I could override that field in a junit
test. But only statically.
Question: how can I reset the WebClient
bean inside a @SpringBootTest
so that it sends the requests always to my mock server?
@Service
public class WebClientService {
public WebClientService(WebClient.Builder builder, @Value("${webclient.url}" String url) {
this.webClient = builder.baseUrl(url)...build();
}
public Response send() {
return webClient.body().exchange().bodyToMono();
}
}
@Service
public void CallingService {
@Autowired
private WebClientService service;
public void call() {
service.send();
}
}
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MyWebTest {
@Autowired
private CallingService calling;
@Test
public void test() {
MockWebServer mockWebServer = new MockWebServer();
System.out.println("Current mock server url: " + mockWebServer.url("/").toString()); //this is random
mockWebServer.enqueue(new MockResponse()....);
//TODO how to make the mocked server url public to the WebClient?
calling.call();
}
}
As you see, I'm writing a full realworld junit integration test. The only problem is: how can I pass the MockWebServer
url and port to the WebClient
so that it automatically sends the requests to my mock??
Sidenote: I definitely need a random port in MockWebServer
here to not interfer with other running tests or applications. Thus have to stick to the random port, and find a way to pass it to the webclient (or dynamically override the application property).
Update: I came up with the following, which works. But maybe anyone knows how to make the mockserver field non-static?
@ContextConfiguration(initializers = RandomPortInitializer.class)
public abstract class AbstractITest {
@ClassRule
public static final MockWebServer mockWebServer = new MockWebServer();
public static class RandomPortInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(applicationContext,
"webclient.url=" + mockWebServer.url("/").toString());
}
}
}
Since Spring Framework 5.2.5 (Spring Boot 2.x) you can use DynamicPropertySource
annotation which is quite handy.
Here is a complete example how you can use it with MockWebServer
to bind the correct port:
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public abstract class AbstractIT {
static MockWebServer mockWebServer;
@DynamicPropertySource
static void properties(DynamicPropertyRegistry r) throws IOException {
r.add("some-service.url", () -> "http://localhost:" + mockWebServer.getPort());
}
@BeforeAll
static void beforeAll() throws IOException {
mockWebServer = new MockWebServer();
mockWebServer.start();
}
@AfterAll
static void afterAll() throws IOException {
mockWebServer.shutdown();
}
}