我想为我的小型应用程序进行 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/