MockMVC returns 200 even if I make a request to a path that does not exist

Operation check environment

Situation where 200 is returned

First, create a simple Controller like this: TodoService is not important in this article, so I will omit it.

Controller.java


@RestController
@RequestMapping("todos") 
public class TodoRestController {
    @Inject
    TodoService todoService;
    @Inject
    Mapper beanMapper;

    @RequestMapping(method = RequestMethod.GET)
    @ResponseStatus(HttpStatus.OK)
    public List<TodoResource> getTodos() {
        Collection<Todo> todos = todoService.findAll();
        List<TodoResource> todoResources = new ArrayList<>();
        for (Todo todo : todos) {
            todoResources.add(beanMapper.map(todo, TodoResource.class));
        }
        return todoResources;
    }
    
    @RequestMapping(method = RequestMethod.POST)
    @ResponseStatus(HttpStatus.CREATED)
    public TodoResource postTodos(@RequestBody @Validated TodoResource todoResource) {
        Todo createdTodo = todoService.create(beanMapper.map(todoResource, Todo.class));
        TodoResource createdTodoResponse = beanMapper.map(createdTodo, TodoResource.class);
        return createdTodoResponse;
    }
}

Let's write a test code for Controller using MockMVC.

Test.java


@RunWith(SpringRunner.class)
@ContextHierarchy({@ContextConfiguration({"classpath:META-INF/spring/applicationContext.xml"}),
    @ContextConfiguration({"classpath:META-INF/spring/spring-mvc-rest.xml"})})
@WebAppConfiguration
public class TodoRestControllerTest {

  @Autowired
  WebApplicationContext webApplicationContext;

  MockMvc mockMvc;

  ObjectMapper mapper;

  @Before
  public void setup() {
    mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
        .addFilter(new XTrackMDCPutFilter(), "/**").alwaysDo(log()).build();
    mapper = new ObjectMapper();
  }
  @Test
  public void notExistPathTest() throws Exception {
    String title = "title";
    TodoResource todoRequest = new TodoResource();
    todoRequest.setTodoTitle(title);
    MvcResult result = mockMvc
        .perform(
            MockMvcRequestBuilders.post("/hage").content(mapper.writeValueAsString(todoRequest))
                .contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
        .andExpect(status().isNotFound()).andReturn();
  }
}

The path is / todos, so if you make a request to / hage, you should get 404, but when you run the test ...

java.lang.AssertionError: Status expected:<404> but was:<200>
	at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:54)
	at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:81)
	at org.springframework.test.web.servlet.result.StatusResultMatchers$10.match(StatusResultMatchers.java:665)
	at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:171)
	at todo.api.TodoRestControllerTest.notExistPathTest(TodoRestControllerTest.java:75)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
	at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
	at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
	at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)

It will be 200 for some reason. : thinking: By the way, if you deploy to the AP server and send a request to / hage, it will be 404 as expected.

The cause is the forward function

I didn't understand the cause at all, but I noticed that I set <mvc: default-servlet-handler /> in the spring-mvc-rest.xml loaded in the test class. ..

This is a setting that enables the function to forward the request received by DispatcherServlet to the default Servlet.

That is, if you make a request to a path that does not exist, MockMVC will forward it to the default Servlet. Since the forward destination is under the jurisdiction of the AP server, ** MockMVC returned 200 because the forward was successful **. So if you remove <mvc: default-servlet-handler />, you will get 404 back.

However, in the case of screen applications that are not REST API, if the forward function is disabled, the static file placed on the AP server cannot be referenced, so the status code is 200 and forward as shown below. Make sure that the destination is the default path.

    MvcResult result = mockMvc
        .perform(
            MockMvcRequestBuilders.post("/hage").content(mapper.writeValueAsString(todoRequest))
                .contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON))
        .andExpect(status().isOk()).andExpect(forwardedUrl("default")).andReturn();
  }

that's all.

in conclusion

For the default Servlet, the article Understanding Access to Static Resources on Spring MVC (+ Spring Boot) is easy to understand.

Recommended Posts

MockMVC returns 200 even if I make a request to a path that does not exist
If hash [: a] [: b] [: c] = 0 in Ruby, I want you to extend it recursively even if the key does not exist.
How to make @Transactional work that does not work if you use it incorrectly
I want to Flash Attribute in Spring even if I set a reverse proxy! (do not do)
Handling when calling a key that does not exist in hash
How to identify the path that is easy to make a mistake
A story that even a man who does not understand C language could add new functions to Ruby 2.6
How to interact with a server that does not crash the app
I tried to make a Web API that connects to DB with Quarkus
A story that suffered from a space that does not disappear even if trimmed with Java The cause is BOM
A note that I gave up trying to make a custom annotation for Lombok
[JPA] Compare table1 and table2 to get and update data that does not exist in table2.
Setting method that the size does not change even if CSS is changed
Even in Java, I want to output true with a == 1 && a == 2 && a == 3 (gray magic that is not so much as black magic)
I did Java to make (a == 1 && a == 2 && a == 3) always true
I wanted to make (a == 1 && a == 2 && a == 3) true in Java
I tried to make a new sorting algorithm, but I don't know if it's really new
How to place a button that does not move by scrolling on TableView etc.
[Java] Dealing with the situation where the program that writes to the Output Stream of Process does not end even if waitFor
I tried to make a login function in Java
I tried to make FizzBuzz that is uselessly flexible
[Java] I tried to make a rock-paper-scissors game that beginners can run on the console.
It's not a big deal if you understand that I was addicted to receiving emails with Java Mail from Exchange Online