Null Page object in Spring Boot REST API unit test execution - java

I have the following REST API controller class.
The endpoint retrieves a paged list of customers.
#RestController
public class CustomerController {
#Autowired
private CustomerRepository customerRepository;
public void setCustomerRepositoryMock(CustomerRepository mockedCustomerRepository) {
this.customerRepository = mockedCustomerRepository;
}
#GetMapping(value="/customers", produces = "application/json")
public ResponseEntity<Page<Customer>> customersList(
#RequestParam(value="pageNumber") int pageNumber,
#RequestParam(value="pageSize") int pageSize){
Pageable customersPageable = PageRequest.of(pageNumber, pageSize);
Page<Customer> customersList = customerRepository.findAll(customersPageable);
return new ResponseEntity<Page<Customer>>(customersList, HttpStatus.OK);
}
}
Now I want to create a mocked unit test for that method.
This is what I have.
public class CustomerControllerTest {
private CustomerRepository mockedCustomerRepository;
private CustomerController customerController;
private Customer customer;
private Page<Customer> customersList;
Pageable customersPageable;
#BeforeEach
void setup() {
customer = new Customer();
customer.setName("Pete");
customer.setAge(35);
customer.setEmail("pete#test.com");
List<Customer> customersListTest = new ArrayList<Customer>();
customersListTest.add(customer);
customersList = new PageImpl<>(customersListTest);
mockedCustomerRepository = mock(CustomerRepository.class);
customerController = new CustomerController();
customerController.setCustomerRepositoryMock(mockedCustomerRepository);
}
#Test
void testListCustomers() {
when(mockedCustomerRepository.findAll(customersPageable)).thenReturn(customersList);
ResponseEntity<Page<Customer>> respPageCustomers = customerController.customersList(0, 3);
assertTrue(respPageCustomers.getBody() != null);
}
}
The problem is that when the following line is executed (in the API method), CustomerList is null.
Page<Customer> customersList = customerRepository.findAll(customersPageable);
But it should have content, because the content was added in the setup method of the test class and then in the following line of the test method.
when(mockedCustomerRepository.findAll(customersPageable)).thenReturn(customersList);

Replace
when(mockedCustomerRepository.findAll(customersPageable)).thenReturn(customersList);
with
when(mockedCustomerRepository.findAll(any(Pageable.class))).thenReturn(customersList);
What you currently have - is that mocked repository will return result only when it receives exact customersPageable (which is null). Using any() will return expected result if any object of mentioned class will be passed as parameter

Related

Declaring mutable fields in spring boot rest controller

The following is my Rest Controller:
#RestController
public class MyController {
#Autowired
private MyService myService;
private boolean deprecatedAPICall = false;
#PostMapping(value = "/deprecatedEndPoint", produces = "application/json")
public ResponseEntity<> deprecatedEndPoint(#RequestBody DeprecatedRequestBody deprecatedRequestBody){
deprecatedAPICall = true;
NewRequestBody newRequestBody = preProcess(deprecatedRequestBody);
result = newEndPoint(newRequestBody);
postProcess(result);
return new ResponseEntity<>(result)
}
#PostMapping(value = "/newEndPoint", produces = "application/json")
public ResponseEntity<> newEndPoint(#RequestBody NewRequestBody newRequestBody){
...
if(deprecatedAPICall) {
deprecatedAPICall = false;
...
}
result = myService.process(newRequestBody);
return new ResponseEntity<>(result)
}
Since the Rest Controller is having a singleton scope, is it a good practise to maintain a mutable field (deprecatedAPICall) in this class? What can possibly go wrong in this class in production?

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;

Junit Mokito Always empty values are returned

I want to return the value of DAO using mokito but it is not working properly.
It doesn't always work.
As you can see from the code below, it sometimes returns the value I want, but sometimes it doesn't know why.
Below is an example of my code.
Example classes
public Myclass {
#Resource(name=userDao)
UserDao userDao;
#Resource(name=infoDao)
InfoDao infoDao;
public boolean checkUserService(UserVo userVo){
int checkCnt = userDao.getUserCnt(userVo);
InfoVo infoVo = new InfoVo();
infoVo.setUserId(...);
infoVo.set....
....
Map<String,Object> userInfo = infoDao.getInfo(infoVo);
return true or false;
}
}
public UserDao {
public <T> int getUserCnt(T value){
}
}
public InfoDao {
public <T> Map<String,Object> getInfo(T vo){
...
}
}
Test Case
#ExtendWith(MockitoExtension.class)
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
public MyclassTest{
#Mock
UserDao userDao ;
#Mock
InfoDao infoDao ;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public testCheckUserService()throws Exception{
UserVo userVo = new UserVo();
when(userDao.getUserCnt(userVo)).thenReturn(1); // This code works normally.
Map<String,Object> checkInfoMap = new HashMap<>();
checkInfoMap.put("name","abc");
InfoVo infoVo = new InfoVo();
//I expected the result to be checkInfoMap, but the empty value will be returned.
when(infoDao.getInfo(infoVo)).thenReturn(checkInfoMap);
//This code likewise always returns an empty value.
when(infoDao.getInfo(anything())).thenReturn(checkInfoMap);
//This code likewise always returns an empty value.
when(infoDao.getInfo(any(InfoVo.class))).thenReturn(checkInfoMap);
}
}
when(userDao.getUserCnt(userVo)).thenReturn(1); This code works, but why doesn't the code below it work?

Test a controller with Junit?

I had some trouble setting up unit test with my spring boot application. My main issue is with the "model" object that's needed in my controller, but I can't find a way to recreate it in my test, which is required to use my function.
here are the function I want to test
#Controller
public class AjoutAbscenceControler {
#Autowired
private AbsenceRepository absenceRepository;
#RequestMapping(value = { "/addAbsence" }, method = RequestMethod.GET)
public String showAddAbsencePage(Model model) {
Absence absence = new Absence();
model.addAttribute("Absence", absence);
return "addAbsence";
}
#RequestMapping(value = { "/addingAbsence" }, method = RequestMethod.POST)
public String saveAbsence(Model model, #ModelAttribute("absence") Absence absence) {
if (absence.getName() != null && absence.getName().length() > 0) {
absenceRepository.save(absence);
}
return "redirect:/userList";
}
}
I did try something like that
#RunWith(MockitoJUnitRunner.class)
public class AjoutAbscenceControlerTest {
#Mock
VacationRepository vacationRepository;
#Mock
CategoryRepository categoryRepository;
#InjectMocks
AjoutAbscenceControler controler;
public MockMvc mockMvc;
#Before
public void setUp() throws Exception{
mockMvc = MockMvcBuilders.standaloneSetup(controler).build();
}
#Test
public void showAddAbsencePagetest() {
AjoutAbscenceControler ajoutAbscenceControler =new AjoutAbscenceControler();
assertEquals("addAbsence",ajoutAbscenceControler.showAddAbsencePage(controler));
}
}
but I don't find any way to create a springfarmwork.ui.Model
If you're testing the logic of your controller you probably shouldn't create a Model object, but mock it, and verify the interactions against it:
#Mock
private Model model;
#Test
public void showAddAbsencePagetest() {
// Should probably be initialized in a #Before method,
// Initialized here for clarity only
AjoutAbscenceControler ajoutAbscenceControler = new AjoutAbscenceControler();
assertEquals("addAbsence", ajoutAbscenceControler.showAddAbsencePage(model));
Mockito.verify(model).addAttribute(eq("Absence"), any(Absence.class));
}

Calling all implementation of an interface within one call

I have an interface and two implementations of that interface.
Now on the interface I am adding '#Component' annotation. One of the implementation has a '#primary' annotation which is only getting called up.
I want to call both the implementations when I call the interface's method from the autowired interface bean.
#Component
public interface CustomerPersister {
AbuserDetails setAbuserDetails(AbuserDetails customer);
}
#Primary
#Component
public class CustomerRedisPersisterImpl implements CustomerPersister{
#Autowired
private CustomerManager customerManager;
#Override
public AbuserDetails setAbuserDetails(AbuserDetails customer) {
if(customerManager.setAbuserDetails
(customer,ATSNamespaces.ABUSERDETAILS)){
return customer;
}else{
return new AbuserDetails();
}
}
#Component
public class MongoDbRepositoryImpl implements CustomerPersister{
#Autowired
MongoTemplate mongoTemplate;
#Override
public AbuserDetails setAbuserDetails(AbuserDetails customer) {
Query query = new Query(Criteria.where("login").is(customer.getLogin()));
System.out.println("query is:"+query);
Update update = new Update();
update.set("isReturnAbuser", customer.getIsReturnAbuser());
update.set("reasonReturnAbuser", customer.getReasonReturnAbuser());
update.set("isCODThrottled", customer.getIsCODThrottled());
update.set("reasonRTOAbuser", customer.getReasonRTOAbuser());
update.set("isFakeEmail", customer.getIsFakeEmail());
update.set("reasonFakeEmail", customer.getReasonFakeEmail());
update.set("amount",customer.getAmount());
WriteResult result = mongoTemplate.upsert(query, update, AbuserDetails.class);
System.out.println("This is the class that got returned:"+result.getClass());
System.out.println("New design result:"+result);
if(result!=null){
if(result.getN() != 0)
return customer;
else
return null;
}else
return null;
}
someOtherClass
#Autowired
private CustomerPersister customerPersister;
#Override
#RequestMapping(value = "/abuser/details/set", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public AbuserDetails setAbuserDetails(#RequestBody AbuserDetails customer){
return customerPersister.setAbuserDetails(customer);
}
You can tell Spring to autowire all implementations of an interface as a List and then call the method an all implementations.
class SomeClass {
List<CustomerPersister> customerPersisters;
#Autowired
SomeClass(List<CustomerPersister> customerPersisters) {
this.customerPersisters = customerPersisters;
}
public void setAbuserDetails(#RequestBody AbuserDetails customer) {
for (CustomerPersister customerPersister: customerPersisters) {
customerPersister.setAbuserDetails(customer);
}
}
}
Of course this will not allow you to return the result of customerPersister.setAbuserDetails(), because you can't return a single value from multiple persister calls. You either have to write some code in SomeClass that will determine which object should be returned or you could return a list of the results from all persisters. Or you have to redesign your interface to match the requirements.

Categories