简体   繁体   中英

MockBean is strange in restful services

I've made rest controller, that calls @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 is handled by 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);
    }
}

Unit controller just calls the 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);
    }
}

Now I need to test them, and created unit test method, that must to check on 404 error:

@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));
    }
}

When I run tests, testGetAllUnits fails:

java.lang.AssertionError: Content type not set

and testUnitNotFound fails with error:

java.lang.AssertionError: Status expected:<404> but was:<201>

But when I remove

@MockBean
UnitService unitService;

It will be working. What the problem?


UPDATE: I have the similar problem now. This code inserts into database info about unit. But I made mock for the method.

    @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));
    }

You're mocking the wrong bean. The bean throwing the exception is the service bean, so mock that.

@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));
}

The problem with the testUnitNotFound() test not working is that you are expecting something from the mocked repository to happen inside a service which is also mocked.

If the service is mocked, then no implementation is invoked. Only a default value is returned which is null . And therefore no exception is thrown as expected...

If you want to have the flexibility of having most of the service mocked but having rest of them having their original implementations called, then you should change the:

@MockBean
UnitService unitService;

into

@SpyBean
UnitService unitService;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM