spring-bootspring-mvcspring-boot-testspring-mvc-test

UriComponentsBuilder/MvcComponentsBuilder usage in a Spring Boot Test


I've put a very simple sample project on GitHub to reproduce the problem.

The main issue is that I have a PersonController that has a PutMapping to create a new person. In order to populate the Location header with the URL to fetch that person, I add the UriComponentsBuilder as parameter for that PutMapping, as you can see here:

  @PostMapping
  public ResponseEntity<Person> add(@RequestBody final PersonForCreate personForCreate, UriComponentsBuilder uriComponentsBuilder) {
    Person newPerson = new Person(this.people.size() + 1, personForCreate.getFirstName(), personForCreate.getLastName());
    this.people.add(newPerson);

    // create the URI for the "Location" header
    MvcUriComponentsBuilder.MethodArgumentBuilder methodArgumentBuilder = MvcUriComponentsBuilder.fromMappingName(uriComponentsBuilder, "getById");
    methodArgumentBuilder.arg(0, newPerson.getId());
    URI uri = URI.create(methodArgumentBuilder.build());

    return ResponseEntity.created(uri).body(newPerson);
  }

This works fine when running the project. But when running a test this results in an IllegalArgumentException No WebApplicationContext. The error comes from the MvcUriComponentsBuilder.fromMappingName call, but I have no idea why.

My test looks as follows:

@ExtendWith(SpringExtension.class)
@WebMvcTest
class PersonControllerTest {

  @Autowired
  private PersonController personController;

  @Test
  void add() {
    this.personController.add(new PersonForCreate("Charles", "Darwin"), UriComponentsBuilder.newInstance());
  }
}

I'm not sure if passing UriComponentsBuilder.newInstance() is correct, but I've tried with other values and notice no difference.

FYI, The sample project uses Spring Boot 2.2.3 and JUnit 5, but I have the same problem using a sample project on JUnit 4.


Solution

  • Did you try MockMvc? The following code will be called in the same way HTTP request gets processed, as you're using @WebMvcTest, only the web layer is invoked rather than the whole context.

    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
    import org.springframework.http.MediaType;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
    import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
    import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
    
    @WebMvcTest
    class PersonControllerTest {
    
        @Autowired
        private MockMvc mockMvc;
    
        @Test
        void add() throws Exception {
            //this.personController.add(new PersonForCreate("Charles", "Darwin"), uriComponentsBuilder);
            this.mockMvc.perform(MockMvcRequestBuilders.post("/person")
                    .content("{\"firstName\": \"Charles\",\"lastName\": \"Darwin\"}").contentType(MediaType.APPLICATION_JSON))
                    .andDo(MockMvcResultHandlers.print())
                    .andExpect(MockMvcResultMatchers.status().isCreated())
                    .andExpect(MockMvcResultMatchers.content().string("{\"id\":4,\"firstName\":\"Charles\",\"lastName\":\"Darwin\"}"));
        }
    }
    

    Spring.io/guides reference

    https://spring.io/guides/gs/testing-web/