MockBean 在 restful 服务中很奇怪
MockBean is strange in restful services
我制作了 rest 控制器,调用@service class:
@Service
public class UnitServiceImpl extends HttpRequestServiceImpl implements UnitService {
@Override
public Unit addUnit(String unitName) {
final Unit unit = new Unit();
unit.setUnitName(unitName);
return unitRepository.save(unit);
}
@Override
public Unit getUnit(int id) {
final Unit unit = unitRepository.findById(id);
if (unit == null) {
throw new EntityNotFoundException("Unit is not found");
}
return unit;
}
@Override
public Iterable<Unit> getAllUnits() {
return unitRepository.findAll();
}
}
EnityNotFoundException 由 ExceptionHandlingController 处理:
@RestController
@ControllerAdvice
public class ExceptionHandlingController extends ResponseEntityExceptionHandler {
@ExceptionHandler({RuntimeException.class})
public final ResponseEntity<ErrorDetails> handleRuntimeException(RuntimeException ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),
request.getDescription(false));
HttpStatus httpStatus = HttpStatus.BAD_REQUEST;
if (ex.getClass() == EntityNotFoundException.class) {
httpStatus = HttpStatus.NOT_FOUND;
}
return new ResponseEntity<>(errorDetails, httpStatus);
}
}
单元控制器只调用getUnit:
@RestController
public class UnitController {
private final UnitService managementService;
@PostMapping(value = "/unit", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Unit> addUnit(HttpServletRequest request) throws FieldsIsAbsentException {
final String unitName = managementService.getParameter(request, "unit_name");
final Unit unit = managementService.addUnit(unitName);
return new ResponseEntity<>(unit, HttpStatus.CREATED);
}
public UnitController(UnitService managementService) {
this.managementService = managementService;
}
@GetMapping(value = "/unit", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Iterable<Unit>> getAllUnits() {
final Iterable<Unit> allUnits = managementService.getAllUnits();
return new ResponseEntity<>(allUnits, HttpStatus.OK);
}
@GetMapping(value = "/unit/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Unit> getUnitById(@PathVariable("id") int id) {
final Unit unit = managementService.getUnit(id);
return new ResponseEntity<>(unit, HttpStatus.CREATED);
}
}
现在我需要测试它们,并创建单元测试方法,必须检查 404 错误:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration
class UnitControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
UnitService unitService;
@MockBean
UnitRepository unitRepository;
@Autowired
private UnitController unitController;
private List<Unit> units;
@Before
public void initUnits() {
units = new ArrayList<>();
Unit unitWithName = new Unit();
unitWithName.setId(1);
unitWithName.setUnitName("NameUnit");
units.add(unitWithName);
Unit unitWithoutName = new Unit();
unitWithoutName.setId(2);
units.add(unitWithoutName);
}
@Test
void contextLoads() {
Assert.assertNotNull(unitController);
}
@Test
void testGetAllUnits() throws Exception {
given(this.unitService.getAllUnits()).willReturn(units);
mockMvc.perform(get("/unit"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
}
@Test
void testUnitNotFound() throws Exception {
int id = -1;
given(this.unitRepository.findById(id)).willReturn(null);
mockMvc.perform(get("/unit/-1"))
.andExpect(status().isNotFound())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
}
}
当我 运行 测试时,testGetAllUnits 失败:
java.lang.AssertionError: Content type not set
并且 testUnitNotFound 失败并出现错误:
java.lang.AssertionError: Status expected:<404> but was:<201>
但是当我删除
@MockBean
UnitService unitService;
它会起作用的。什么问题?
更新:
我现在有类似的问题。此代码将有关单元的信息插入到数据库中。但是我为这个方法做了模拟。
@Test
void testAddUnit() throws Exception {
Unit unit = new Unit();
unit.setId(1);
unit.setUnitName("TestUnit");
given(unitService.addUnit("TestUnit")).willReturn(unit);
mockMvc.perform(post("/unit").param("unit_name", "TestUnit"))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.unitName").value("TestUnit"))
.andExpect(jsonPath("$.id").value(1));
}
testUnitNotFound()
测试不起作用的问题在于,您期望模拟存储库中的某些内容发生在同样被模拟的服务中。
如果服务被模拟,则不会调用任何实现。仅返回默认值 null
。因此没有像预期的那样抛出异常...
如果您想灵活地模拟大部分服务,但让其余服务调用其原始实现,那么您应该更改:
@MockBean
UnitService unitService;
进入
@SpyBean
UnitService unitService;
你在嘲笑错误的豆子。抛出异常的 bean 是服务 bean,所以模拟它。
@Test
void testUnitNotFound() throws Exception {
int id = -1;
given(this.service.getUnit(id)).willThrow(new EntityNotFoundException("Unit is not found"));
mockMvc.perform(get("/unit/-1"))
.andExpect(status().isNotFound())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
}
我制作了 rest 控制器,调用@service class:
@Service
public class UnitServiceImpl extends HttpRequestServiceImpl implements UnitService {
@Override
public Unit addUnit(String unitName) {
final Unit unit = new Unit();
unit.setUnitName(unitName);
return unitRepository.save(unit);
}
@Override
public Unit getUnit(int id) {
final Unit unit = unitRepository.findById(id);
if (unit == null) {
throw new EntityNotFoundException("Unit is not found");
}
return unit;
}
@Override
public Iterable<Unit> getAllUnits() {
return unitRepository.findAll();
}
}
EnityNotFoundException 由 ExceptionHandlingController 处理:
@RestController
@ControllerAdvice
public class ExceptionHandlingController extends ResponseEntityExceptionHandler {
@ExceptionHandler({RuntimeException.class})
public final ResponseEntity<ErrorDetails> handleRuntimeException(RuntimeException ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),
request.getDescription(false));
HttpStatus httpStatus = HttpStatus.BAD_REQUEST;
if (ex.getClass() == EntityNotFoundException.class) {
httpStatus = HttpStatus.NOT_FOUND;
}
return new ResponseEntity<>(errorDetails, httpStatus);
}
}
单元控制器只调用getUnit:
@RestController
public class UnitController {
private final UnitService managementService;
@PostMapping(value = "/unit", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Unit> addUnit(HttpServletRequest request) throws FieldsIsAbsentException {
final String unitName = managementService.getParameter(request, "unit_name");
final Unit unit = managementService.addUnit(unitName);
return new ResponseEntity<>(unit, HttpStatus.CREATED);
}
public UnitController(UnitService managementService) {
this.managementService = managementService;
}
@GetMapping(value = "/unit", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Iterable<Unit>> getAllUnits() {
final Iterable<Unit> allUnits = managementService.getAllUnits();
return new ResponseEntity<>(allUnits, HttpStatus.OK);
}
@GetMapping(value = "/unit/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Unit> getUnitById(@PathVariable("id") int id) {
final Unit unit = managementService.getUnit(id);
return new ResponseEntity<>(unit, HttpStatus.CREATED);
}
}
现在我需要测试它们,并创建单元测试方法,必须检查 404 错误:
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@ContextConfiguration
class UnitControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
UnitService unitService;
@MockBean
UnitRepository unitRepository;
@Autowired
private UnitController unitController;
private List<Unit> units;
@Before
public void initUnits() {
units = new ArrayList<>();
Unit unitWithName = new Unit();
unitWithName.setId(1);
unitWithName.setUnitName("NameUnit");
units.add(unitWithName);
Unit unitWithoutName = new Unit();
unitWithoutName.setId(2);
units.add(unitWithoutName);
}
@Test
void contextLoads() {
Assert.assertNotNull(unitController);
}
@Test
void testGetAllUnits() throws Exception {
given(this.unitService.getAllUnits()).willReturn(units);
mockMvc.perform(get("/unit"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
}
@Test
void testUnitNotFound() throws Exception {
int id = -1;
given(this.unitRepository.findById(id)).willReturn(null);
mockMvc.perform(get("/unit/-1"))
.andExpect(status().isNotFound())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
}
}
当我 运行 测试时,testGetAllUnits 失败:
java.lang.AssertionError: Content type not set
并且 testUnitNotFound 失败并出现错误:
java.lang.AssertionError: Status expected:<404> but was:<201>
但是当我删除
@MockBean
UnitService unitService;
它会起作用的。什么问题?
更新: 我现在有类似的问题。此代码将有关单元的信息插入到数据库中。但是我为这个方法做了模拟。
@Test
void testAddUnit() throws Exception {
Unit unit = new Unit();
unit.setId(1);
unit.setUnitName("TestUnit");
given(unitService.addUnit("TestUnit")).willReturn(unit);
mockMvc.perform(post("/unit").param("unit_name", "TestUnit"))
.andExpect(status().isCreated())
.andExpect(jsonPath("$.unitName").value("TestUnit"))
.andExpect(jsonPath("$.id").value(1));
}
testUnitNotFound()
测试不起作用的问题在于,您期望模拟存储库中的某些内容发生在同样被模拟的服务中。
如果服务被模拟,则不会调用任何实现。仅返回默认值 null
。因此没有像预期的那样抛出异常...
如果您想灵活地模拟大部分服务,但让其余服务调用其原始实现,那么您应该更改:
@MockBean
UnitService unitService;
进入
@SpyBean
UnitService unitService;
你在嘲笑错误的豆子。抛出异常的 bean 是服务 bean,所以模拟它。
@Test
void testUnitNotFound() throws Exception {
int id = -1;
given(this.service.getUnit(id)).willThrow(new EntityNotFoundException("Unit is not found"));
mockMvc.perform(get("/unit/-1"))
.andExpect(status().isNotFound())
.andExpect(content().contentType(MediaType.APPLICATION_JSON));
}