0
@SpringBootTest(properties = "spring.mvc.servlet.path=/test/path")
@AutoConfigureMockMvc
public class MyTest {
   @Autowired
   private WebTestClient webTestClient

   @Test
   public void test() {
       webTestClient.post()
                .uri(URL)
                .bodyValue(json)
                .exchange()
                .expectStatus().isOk()
                .expectBody(String.class)
                .returnResult()
                .getResponseBody();
   }
}

@RestController
public class MyController {    
    @PostMapping
    public Object post(HttpServletRequest req) {
        System.out.println(req.getServletPath()); //always empty in tests
    }
}

This creates a MockHttpServletRequest that is send to the @RestContoller servlets.

Problem: my servlets make use of HttpServletRequest.getServletPath(), but which is always empty using the WebTestClient approach above.

Question: how can I explicit set the servletPath in my junit tests?

membersound
  • 81,582
  • 193
  • 585
  • 1,120
  • what about mocking `HttpServletRequest.getServletPath()` method using Mockito or something? do you need other values from httpServletRequest? – Shekhar Rai Feb 15 '22 at 11:27
  • Well but I don't create the `MockHttpServletRequest` / `HttpServletRequest` myself. It is created by the `WebTestClient` builder process internally by Spring... I'd somehow have to find a way to intercept the `MockHttpServletRequest` before it is send by `WebTestClient`. – membersound Feb 15 '22 at 11:29
  • If it is being created as an empty object, and mocking it can override the object then you should try it right? – Shekhar Rai Feb 15 '22 at 11:33
  • Is it possible to set `spring.mvc.servlet.path` to some fixed value in the test environment? – stevecross Feb 15 '22 at 11:34
  • @stevecross yes that's possible, but still the `HttpServletRequest.getServletPath()` will be empty then. – membersound Feb 15 '22 at 11:36
  • Can you try mocking `org.springframework.http.server.reactive.ServerHttpRequest` instead of `javax.servlet.http.ServletHttpRequest`? [source](https://stackoverflow.com/a/50460097/5788486) – Shekhar Rai Feb 15 '22 at 11:45
  • Can you show us how you are configuring the test, and how you create the `WebTestClient`? Because for me setting `spring.mvc.servlet.path` works. – stevecross Feb 15 '22 at 11:51
  • @stevecross see my edit, added full configuration. – membersound Feb 15 '22 at 12:13
  • I see. I put `webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT` on `@SpringBootTest` instead of using `@AutoConfigureMockMvc`. – stevecross Feb 15 '22 at 12:21
  • But don't I have to use `@AutoConfigureMockMvc` for `WebTestClient` being properly injected? Btw also adding the `webEnvironment` did not help! – membersound Feb 15 '22 at 12:22
  • As far as I know, no. I think using `@AutoConfigureMockMvc` is actually the issue here, because it creates a mocked servlet environment whereas `webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT` creates a real servlet environment that listens on a random port. And it seems like the mock environment ignores the value of `spring.mvc.servlet.path`. – stevecross Feb 15 '22 at 12:28
  • Unfortunately that's not working. The `WebTestClient` request will never reach the `@RestController` without `@AutoConfigureMockMvc`, as tested. – membersound Feb 15 '22 at 12:32
  • The `spring.mvc.servlet.path` should work, however your RANDOM PORT and autoconfigure mock mvc is a bit of a contradiction. Do you want to mock or use a port? Imho RANDOM_PORT should be MOCKED. And instead of `@AutoConfigureMockMvc` you should use `@AutoConfigureWebTestClient` as that is what it seems you want to do. – M. Deinum Feb 15 '22 at 14:56
  • @M.Deinum `@AutoConfigureWebTestClient` is not enough: `Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.test.web.reactive.server.WebTestClient'`. So actually `@AutoConfigureMockMvc` is required explicit. – membersound Feb 15 '22 at 15:03
  • It should be enough as that is for configuring the webtestclient, although it is in the reactive package so it might have some additional conditions. Looking at the sources the `setServletPath` is never explicitly set on the `MockHttpServletRequest` so this might be an ommision in the test support (auto-config in Spring Boot, it should align with the `spring.mvc.servlet.path` property imho). – M. Deinum Feb 15 '22 at 15:46

2 Answers2

0

I could solve it as follows, but as this is really hacky, I'd still appreciate a proper solution.

@TestConfiguration
static class ServletPathMockConfiguration {
    @Bean
    public Filter filter() {
        return (request, response, filterchain) -> {
            Object req = request;
            while (req instanceof ServletRequestWrapper) {
                req = ((ServletRequestWrapper) req).getRequest();
            }
            
            if (req instanceof MockHttpServletRequest)
                ((MockHttpServletRequest) req).setServletPath("/junit/mock/path");

            filterchain.doFilter(request, response);
        };
    }
}
membersound
  • 81,582
  • 193
  • 585
  • 1,120
-1

A solution is to compute the path : request.getServletPath() returned null from Spring MVC

Did the job for me, in tests and when it's in prod.

Maelig
  • 2,046
  • 4
  • 24
  • 49