4

A Spring Boot application provides a REST API. It has several containers and is unit-tested. I have modified this application to add a new controller with a new PUT route, to upload a file.

When I run this application with mvn spring-boot:run, I can access the new route via Postman. However, in my unit tests, I cannot.

My unit test autowires a WebApplicationContext into the test and creates a MockMVC from it, like so:

@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
class NewControllerTest {

    @Autowired
    private WebApplicationContext ctx;

    // ... more autowirings ...

    private MockMvc mockMvc;

    @Before
    public void setUp() {
        mockMvc = standaloneSetup(ctx).build();
    }

    // ... actual test cases ...

}

Then, within the test case, I use the route like this:

mockMvc.perform(put("/newctrl/route/" + param + "/img.jpg")
        .contentType("application/octet-stream").content(data))
    .andExpect(status().isOk());

where data is a byte[] array and param is an integer generated at run time.

The API is accesed nearly the same way in several other test cases. The only difference is that in the other test cases, the request method is never PUT, and the content is always a string. All other test cases work.

My new test, however, produces an error message:

java.lang.AssertionError: Status expected:<200> but was:<404>

In the log, I find:

2016-11-27 20:01:22.263  WARN 15648 --- [           main] o.s.web.servlet.PageNotFound             : No mapping found for HTTP request with URI [/newctrl/route/42/img.jpg] in DispatcherServlet with name ''

I use mvn spring-boot:run and copy-paste the URL from the log entry to Postman. I attach an image and, voilá, it works.

Why, then, does my test not work?

EDIT: Excerpts from NewController:

@RequestMapping("/newctrl")
@RestController
@RequiredArgsConstructor(onConstructor = @__({@Autowired}))
public class NewController {

    @RequestMapping(value = "/route/{param}/{filename:.+}")
    public void theRoute(@PathVariable Long param, @PathVariable String filename,
            HttpServletRequest request, HttpServletResponse response) throws IOException {
        // ...
    }

}
elaforma
  • 652
  • 1
  • 9
  • 29
  • 1
    We need to see your request mappings. It's also usually helpful to turn up logging on the Dispatcher Servlet to DEBUG to see what it's mapping in; you may not be including your controller in your test configuration. – chrylis -cautiouslyoptimistic- Nov 27 '16 at 19:42
  • @chrylis I edited the question to include the information on the request mappings. I also turned on DEBUG logging for the Dispatcher Servlet, but it produces no output. However, if you mean the line where it says `Mapped "{[/newctrl/route/{goeswith}/{filename:.+}]}" onto public void somepackage.NewController.theRoute(java.lang.Long,java.lang.String,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.io.IOException`, yes, that happens during the first test. – elaforma Nov 27 '16 at 19:58
  • Yes, that's one of the two main lines in question. – chrylis -cautiouslyoptimistic- Nov 27 '16 at 19:59
  • (that would be in s.w.s.m.m.a.RequestMappingHandlerMapping) – elaforma Nov 27 '16 at 19:59
  • Note that as of Spring 4.3 the `@Autowired` annotation is no longer required for a single constructor. – chrylis -cautiouslyoptimistic- Nov 27 '16 at 19:59
  • @chrylis Thanks, I will inform my project team about `@Autowired`. – elaforma Nov 27 '16 at 20:01
  • Are you sure that that's the entire mapping line? Nothing about content types or HTTP verbs? What happens if you use `accepts=MediaType.APPLICATION_OCTET_STREAM_TYPE` (or whatever it is)? – chrylis -cautiouslyoptimistic- Nov 27 '16 at 20:01
  • There are no other parameters to `@RequestMapping`. I have removed a `method = RequestMethod.PUT` because I initially suspected it of causing trouble. When I add `accepts = MediaType.APPLICATION_OCTET_STREAM`, I only get an error. What still puzzles me about this is that everything works in Postman. – elaforma Nov 27 '16 at 20:05
  • To add, the error I get when I add the `accepts` is a compile-time error, `Attribute value must be constant` – elaforma Nov 27 '16 at 20:06
  • @chrylis You mentioned earlier that the `Mapped ...` line is one of two important lines in question. Which other important line did you expect, so that I can verify its presence? – elaforma Nov 27 '16 at 20:13
  • Does it work with the `GET` method? Your `@RequestMapping` is missing `method=PUT`, maybe that is the problem? – lexicore Nov 27 '16 at 20:34
  • I was looking for the log line where the dispatcher indicates either which controller method it's chosen or that it can't find one. – chrylis -cautiouslyoptimistic- Nov 27 '16 at 20:39
  • Check the docs. That `accepts` might should be `consumes`. I don't have a Spring project open at the moment. – chrylis -cautiouslyoptimistic- Nov 27 '16 at 20:40
  • @chrylis You mean `No mapping found ...`? It is in my post. – elaforma Nov 27 '16 at 21:01
  • As for the `accepts` vs `consumes`, it probably is `consumes`, but the main problem was me using `MediaType.APPLICATION_OCTET_STREAM` rather than `MediaType.APPLICATION_OCTET_STREAM_VALUE`, which screwed up Lombok annotation processing in the entire project. (Another question for another time: How can this screw up annotation processing for the entire project?) – elaforma Nov 27 '16 at 21:05
  • And no, adding `consumes` does not change anything. – elaforma Nov 27 '16 at 21:06
  • @lexicore I used to have `method=PUT` there, and it did not work either. Also, the documentation disagrees with you on it being required: http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html#method-- – elaforma Nov 27 '16 at 21:08
  • @fNek "required in annotation" vs. "required to make things work". So, does your test work if you GET instead of PUT? Have you tried starting the test, pausing it with a breakpoint and `curl`ing to the target URL? I think I had "No mapping" type of errors when trying the wrong method. – lexicore Nov 27 '16 at 22:09
  • What I tried is the following: Run the whole thing with `mvn spring-boot:run`, execute the PUT request via Postman... and it worked. The only problem is that it doesn't work in the test. I have no use for something that works only with a GET request. And when no `method` is specified, the default is that all methods are allowed. – elaforma Nov 27 '16 at 22:30
  • Missed, it, sorry. And the only thing I can think of is that you're not mapping in the controller; since you are, I'm stumped. – chrylis -cautiouslyoptimistic- Nov 28 '16 at 01:14

0 Answers0