java - 获取 NestedServletException : Request processing failed; nested exception is java. lang.IllegalStateException:映射的处理程序方法不明确

标签 java mongodb spring-boot junit mockmvc

我想为我的小型应用程序进行 JUnit 5 集成测试。

问题发生在使用 MockMvc 使用 @PathVariable 发送 GET 请求的测试中。

我的 Controller 看起来像:

@RestController
@RequestMapping("/device")
public class DeviceController {

  private DeviceService deviceService;

  public DeviceController(DeviceService deviceService) {
    this.deviceService = deviceService;
  }

  @GetMapping
  public ResponseEntity<Set<DeviceDto>> findAllDevices() {
    return ResponseEntity.ok(deviceService.findAllDevices());
  }

  @RequestMapping(method = RequestMethod.GET, value = "/{id}")
  public ResponseEntity<Optional<DeviceDto>> findDeviceById(
      @PathVariable(value = "id") String id) {
    return ResponseEntity.ok(deviceService.findDeviceById(id));
  }

  @GetMapping("/{vendor}")
  public ResponseEntity<Set<DeviceDto>> findDevicesByVendor(@PathVariable String vendor) {
    return ResponseEntity.ok(deviceService.findAllDevicesByVendor(vendor));
  }

  @GetMapping("/{model}")
  public ResponseEntity<Set<DeviceDto>> findDevicesByModel(@PathVariable String model) {
    return ResponseEntity.ok(deviceService.findAllDevicesByModel(model));
  }

  @GetMapping("/preview")
  public ResponseEntity<Set<DeviceDtoPreview>> findDevicesWithIpAndSubnetMask() {
    return ResponseEntity.ok(deviceService.findAllDevicesForPreview());
  }
}

MongoDB 实体看起来像:

@Document
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Builder
public class Device {

  @Id
  @GeneratedValue(generator = "uuid")
  public String id;

  @Field("manufacturer")
  public String vendor;
  @Field("model")
  public String model;
  @Field("serialNumber")
  public String serNum;
  @Field("macAddress")
  private String mac;
  @Field("ip")
  private String ip;
  @Field("subnetMask")
  private String netMask;
}

存储库:

public interface DeviceRepository extends MongoRepository<Device, String> {

  Set<Device> findAllByVendor(String vendor);

  Set<Device> findAllByModel(String model);

}

服务方式:


public Optional<DeviceDto> findDeviceById(final String id) {
    return deviceRepository
        .findById(id)
        .map(DeviceMapper.MAPPER::deviceToDeviceDto);
  }

  public Set<DeviceDto> findAllDevicesByVendor(final String vendor) {
    return deviceRepository.findAllByVendor(vendor)
        .stream()
        .map(DeviceMapper.MAPPER::deviceToDeviceDto)
        .collect(Collectors.toSet());
  }

  public Set<DeviceDto> findAllDevicesByModel(final String model) {
    return deviceRepository.findAllByModel(model)
        .stream()
        .map(DeviceMapper.MAPPER::deviceToDeviceDto)
        .collect(Collectors.toSet());
  }

我的 ControllerTest 看起来:

package com.interview.exercise.controller;

import static org.mockito.BDDMockito.given;

import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;

@ExtendWith(SpringExtension.class)
@WebMvcTest(controllers = DeviceController.class)
class DeviceControllerTest {

  @Autowired
  private MockMvc mockMvc;

  @MockBean
  private DeviceRepository deviceRepository;

  @MockBean
  private DeviceService deviceService;

  @Test
  void addDevice() {
  }

  @Test
  void findAllDevicesOnGetRequest() throws Exception {
    var device1 = DeviceDto.builder().id(UUID.randomUUID().toString()).ip("192.168.0.101 ")
        .netMask("255.255.0.1 ").model("model1").vendor("vendor1").build();

    var device2 = DeviceDto.builder().id(UUID.randomUUID().toString()).ip("192.168.0.102 ")
        .netMask("255.255.0.2").model("model2").vendor("vendor2").build();

    var device3 = DeviceDto.builder().id(UUID.randomUUID().toString()).ip("192.168.0.103 ")
        .netMask("255.255.0.3 ").model("model3").vendor("vendor3").build();
    var devices = Set.of(device1, device2, device3);
    given(deviceService.findAllDevices()).willReturn(devices);

    ResultActions resultActions = mockMvc.perform(MockMvcRequestBuilders.get("/device")
        .contentType(
            MediaType.APPLICATION_JSON))
        .andExpect(MockMvcResultMatchers.status().isOk())
        .andExpect(MockMvcResultMatchers.jsonPath("$",
            Matchers.hasSize(3)));

  }

  @Test
  void findDeviceByIdOnGetRequest() throws Exception {
    var device = DeviceDto.builder().id("1").ip("192.168.0.101")
        .netMask("255.255.0.1 ").model("model1").vendor("vendor1").build();

    given(deviceService.findDeviceById("1")).willReturn(Optional.of(device));
    ResultActions resultMatchers = mockMvc.perform(MockMvcRequestBuilders
        .get("/device/{id}", "1")
        .contentType(MediaType.APPLICATION_JSON))
        .andExpect(MockMvcResultMatchers.status().isOk())
        .andExpect(MockMvcResultMatchers.jsonPath("$.model", Matchers.equalTo(device.getModel())));

  }

  @Test
  void findDevicesByVendorOnGetRequest() throws Exception {

    var device1 = DeviceDto.builder().id(UUID.randomUUID().toString()).ip("192.168.0.101 ")
        .netMask("255.255.0.1 ").model("model1").vendor("vendor1").build();

    var device2 = DeviceDto.builder().id(UUID.randomUUID().toString()).ip("192.168.0.102 ")
        .netMask("255.255.0.2").model("model2").vendor("vendor2").build();

    var device3 = DeviceDto.builder().id(UUID.randomUUID().toString()).ip("192.168.0.103 ")
        .netMask("255.255.0.3 ").model("model3").vendor("vendor3").build();
    var devices = Set.of(device1, device2, device3);
    given(deviceService.findAllDevicesByVendor("vendor3")).willReturn(devices);

    ResultActions resultActions = mockMvc
        .perform(MockMvcRequestBuilders
            .get("/device/{vendor}", "vendor3")
            .contentType(MediaType.APPLICATION_JSON))
        .andExpect(MockMvcResultMatchers.status().isOk())
        .andExpect(MockMvcResultMatchers.jsonPath("$",
            Matchers.hasSize(1)));

  }

  @Test
  void findDevicesWithIpAndSubnetMaskOnGetRequest() throws Exception {
    var device1 = DeviceDtoPreview.builder().id(UUID.randomUUID().toString()).ip("192.168.0.101 ")
        .netMask("255.255.0.1 ").build();

    var device2 = DeviceDtoPreview.builder().id(UUID.randomUUID().toString()).ip("192.168.0.102 ")
        .netMask("255.255.0.2").build();

    var device3 = DeviceDtoPreview.builder().id(UUID.randomUUID().toString()).ip("192.168.0.103 ")
        .netMask("255.255.0.3 ").build();

    var device4 = DeviceDtoPreview.builder().id(UUID.randomUUID().toString()).ip("192.168.0.103 ")
        .netMask("255.255.0.3 ").build();
    var devices = Set.of(device1, device2, device3, device4);
    given(deviceService.findAllDevicesForPreview()).willReturn(devices);

    ResultActions resultActions = mockMvc
        .perform(MockMvcRequestBuilders.get("/device/preview").contentType(
            MediaType.APPLICATION_JSON))
        .andExpect(MockMvcResultMatchers.status().isOk())
        .andExpect(MockMvcResultMatchers.jsonPath("$",
            Matchers.hasSize(4)));
  }

  @Test
  void findDevicesByModelOnGetRequest() throws Exception {
    var device1 = DeviceDto.builder().id(UUID.randomUUID().toString()).ip("192.168.0.101 ")
        .netMask("255.255.0.1 ").model("model1").vendor("vendor1").build();

    var device2 = DeviceDto.builder().id(UUID.randomUUID().toString()).ip("192.168.0.102 ")
        .netMask("255.255.0.2").model("model2").vendor("vendor2").build();

    var device3 = DeviceDto.builder().id(UUID.randomUUID().toString()).ip("192.168.0.103 ")
        .netMask("255.255.0.3 ").model("model3").vendor("vendor3").build();

    var device4 = DeviceDto.builder().id(UUID.randomUUID().toString()).ip("192.168.0.103 ")
        .netMask("255.255.0.3 ").model("model2").vendor("vendor4").build();
    var devices = Set.of(device1, device2, device3, device4);
    given(deviceService.findAllDevicesByModel("model2")).willReturn(devices);

    ResultActions resultActions = mockMvc
        .perform(MockMvcRequestBuilders.get("/device/{model}", "model2").contentType(
            MediaType.APPLICATION_JSON))
        .andExpect(MockMvcResultMatchers.status().isOk())
        .andExpect(MockMvcResultMatchers.jsonPath("$",
            Matchers.hasSize(4)));

  }

}

不带@PathVariable的测试通过,但包含@PathVariable的测试出现问题。我尝试以不同的方式使用@RequestMapping,但它仍然产生相同的错误。

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalStateException: Ambiguous handler methods mapped for '/device/model2': {public org.springframework.http.ResponseEntity com.interview.exercise.controller.DeviceController.findDeviceById(java.lang.String), public org.springframework.http.ResponseEntity com.interview.exercise.controller.DeviceController.findDevicesByModel(java.lang.String)}

我非常确信,过去我正在创建类似的测试,一切都很好,但现在我无法在集成测试中使用 GET 请求达到目标。我将不胜感激如何修复我的mockMvc.perform()方法以达到理想效果的建议。

最佳答案

我不相信 Spring 能够区分 @GetMapping("/{vendor}")@GetMapping("/{model}")。两者都具有 @PathVariable 类型(字符串)和基本路径 /device

为了帮助 Spring 正确地将请求映射到 Controller 方法,请在 Controller 中尝试如下操作:

 @GetMapping("vendor/{vendorID}")
  public ResponseEntity<Set<DeviceDto>> findDevicesByVendor(@PathVariable String vendorID) {
    return ResponseEntity.ok(deviceService.findAllDevicesByVendor(vendorID));
  }

  @GetMapping("model/{modelID}")
  public ResponseEntity<Set<DeviceDto>> findDevicesByModel(@PathVariable String modelID) {
    return ResponseEntity.ok(deviceService.findAllDevicesByModel(modelID));
  }

关于java - 获取 NestedServletException : Request processing failed; nested exception is java. lang.IllegalStateException:映射的处理程序方法不明确,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57741334/

相关文章:

java - 在android中打开SQLite数据库文件

java - 如何使用iText从书签在PDF文件中创建目录页?

java - JAVA中的正则表达式最多一个点

spring-mvc - Gradle如何配置构建文件以从同一项目创建Spring Boot jar和Spring Web-MVC War

java - 如何解决自定义存储库 JPA 中的错误 "error in your SQL syntax"

java - 空格前第一个字符的正则表达式

node.js - 如何在 Nodejs 中使用异步

mysql - 什么是存储对象更改历史的好解决方案?

node.js - Meteor Mongo BulkOp 将 ObjectID 转换为普通对象

java - SpringBoot 2.2.1 groovyMarkupConfigurer 异常