java - 如何使用 MockMvc 测试 REST Controller

标签 java spring-boot mocking mockito integration-testing

我已经尝试使用 MockMvcMockitoCucumber 来测试我的 REST Controller 端点有一段时间了。

  • 我的目标是测试我的服务层,而不调用实际的实现。 (所以我不希望数据出现在数据库中)

  • 我希望避免使用“内存”数据库,因为我正在处理大型项目。

我最近让它工作,没有模拟,但由于我尝试模拟我的测试,我一直收到NestedServletException:请求处理失败;嵌套异常是java.lang.NullPointerException

AddressController 代码段

@Autowired
private AddressManager addressManager;

@GetMapping(value = "/{id}")
public ResponseEntity<Object> getAddress(@PathVariable("id") Long addressId) {
    return new ResponseEntity<>(addressManager.getAddress(addressId), HttpStatus.OK);
// getAddress calls a data manager layer which then calls addressRepo.findOneById(addressId);
}

@PostMapping(value = "/add")
public ResponseEntity<Object> addAddress(@RequestBody Address address) {
    return new ResponseEntity<>(addressManager.addAddress(address), HttpStatus.OK);
// addAddress calls a data manager layer which then calls addressRepo.save(address);
}

AddressStepDefs 代码段

@RunWith(MockitoJUnitRunner.class) 
@SpringBootTest(webEnvironment= WebEnvironment.MOCK)
@Transactional
@AutoConfigureMockMvc
public class AddressStepDefs {

    private MockMvc mockMvc;

    private ResultActions result; // allows to track result

    @InjectMocks
    private AddressController addressController; 

    @Mock
    private AddressDataManager addressService;

   // given step

   @Before  
   public void setup() throws IOException {
       // must be called for the @Mock annotations to be processed and for the mock service to be injected 
       // into the controller under test.
      MockitoAnnotations.initMocks(this);
      this.mockMvc = MockMvcBuilders.standaloneSetup(new AddressController()).build(); 
   }

   @When("I add a new Address using POST at {string} with JSON:")
   public void i_add_a_new_Address_using_POST_at_with_JSON(String request, String json) throws Exception {
       /** Build a POST request using mockMvc **/
       result = this.mockMvc.perform(post(request).contentType(MediaType.APPLICATION_JSON)
                .content(json.getBytes()).characterEncoding("utf-8"));
    }

    @Then("the response code should be OK {int} and the resulting json should be:")
    public void the_response_code_should_be_OK_and_the_resulting_json_should_be(Integer responseCode, 
    String json) throws Exception {
        result.andExpect(status().is(responseCode));
        result.andExpect(content().string(json));
    }

    @When("I request to view an Address with id {int} at {string}")
    public void i_request_to_view_an_Address_with_id_at(Integer id, String request) throws Exception {
        /** Build a GET request **/
        result = this.mockMvc.perform(get(request + id).contentType(MediaType.APPLICATION_JSON));
    }

最佳答案

假设您使用的是最新版本的 Spring Boot(并且您还不需要为此使用 Cucumber),那么您只需要 AddressStepDefs 如下:

@WebMvcTest(AddressController.class)
public class AddressStepDefs {
  @MockBean
  private AddressDataManager addressService;

  @Autowired
  private MockMvc mvc;

  ...

  // Depending on how you configured your Spring beans, you might need this; try first without it ;)
  @Configuration
  @ComponentScan(basePackageClasses = AddressController.class)
  static class TestConfig {
    // ...will be used instead of the application's primary configuration
  }
}

@WebMvcTest annotation is convenient here for your use case since it's only used for Spring MVC tests that focuses only on Spring MVC components.

然后给定的测试可以写成:

@Test
void getAll_WhenRecordsExist() throws Exception { // HTTP 200 (OK)
  final Collection<Address> expected = Arrays.asList(AddressFactory.random(), AddressFactory.random());
  Mockito.when(addressService.searchAll()).thenReturn(expected);
  mvc.perform(get("/addresses").accept(MediaType.APPLICATION_JSON))
     // .andDo(MockMvcResultHandlers.print())
      .andExpect(header().string(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE))
      .andExpect(status().isOk())
      .andExpect(content().json(mapper.writeValueAsString(expected))); // ...you need Jackson's object mapper injected also as part of a class' member
  Mockito.verify(service).searchAll();
}

If you are mocking addressService this is not an integration test, IMHO.

关于java - 如何使用 MockMvc 测试 REST Controller ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60492301/

相关文章:

.net - TDD 入门?

objective-c - 使用 OCMock 测试嵌套错误处理

Java 初始化嵌入另一个 map 的 map

java - 从 Java Date 对象中去除时间的最有效方法是什么?

java - Spring Boot Cache + Apache ignite + Spring Boot Actuator - 运行测试时应用程序无法启动

java - 需要帮助从 web.xml spring boot 转换

javascript - 如何将Sinon.js FakeXMLHttpRequest与superagent一起使用?

java - 如何让kafka在java程序中消耗滞后

java - 如何修改spring boot jar中的属性文件

json - 需要地址的 AWS Cognito NEW_PASSWORD_REQUIRED 挑战,但它似乎不接受任何形式的地址类型