Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set MockWebServer port to WebClient in JUnit test?

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());
        }
    }
}
like image 547
membersound Avatar asked Jul 24 '19 15:07

membersound


People also ask

How do I use mock server in JUnit?

MockServer can be run:using a JUnit 4 @Rule via a @Rule annotated field in a JUnit 4 test. using a JUnit 5 Test Extension via a @ExtendWith annotated JUnit 5 class. using a Spring Test Execution Listener via a @MockServerTest annotated test class. as a Docker container in any Docker enabled environment.


1 Answers

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();
    }
}
like image 73
magiccrafter Avatar answered Sep 28 '22 07:09

magiccrafter