Testing MockBean Null - spring

I have this class definition
#RestController
public class ReservationController {
#Autowired
private Reservation reservation;
#RequestMapping(value = "/reservation", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.POST)
#ResponseBody
public Reservation getReservation() {
return reservation;
}
}
where Reservation is a simple Pojo
public class Reservation {
private long id;
private String reservationName;
public Reservation() {
super();
this.id = 333;
this.reservationName = "prova123";
}
public Reservation(long id, String reservationName) {
super();
this.id = id;
this.reservationName = reservationName;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getReservationName() {
return reservationName;
}
public void setReservationName(String reservationName) {
this.reservationName = reservationName;
}
#Override
public String toString() {
return "Reservation [id=" + id + ", reservationName=" + reservationName + "]";
}
}
when I try to test this class
#WebMvcTest
#RunWith(SpringRunner.class)
public class MvcTest {
#Autowired
private MockMvc mockMvc;
#MockBean(name = "reservation")
private Reservation reservation;
#Test
public void postReservation() throws Exception {
mockMvc.perform(MockMvcRequestBuilders.post("/reservation"))
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(MockMvcResultMatchers.status().isOk());
}
}
I got this error:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.http.converter.HttpMessageConversionException: Type definition error: [simple type, class org.mockito.internal.debugging.LocationImpl]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.mockito.internal.debugging.LocationImpl and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: spring.boot.usingSpringBoot.entity.Reservation$MockitoMock$980801978["mockitoInterceptor"]->org.mockito.internal.creation.bytebuddy.MockMethodInterceptor["mockHandler"]->org.mockito.internal.handler.InvocationNotifierHandler["invocationContainer"]->org.mockito.internal.stubbing.InvocationContainerImpl["invocationForStubbing"]->org.mockito.internal.invocation.InvocationMatcher["invocation"]->org.mockito.internal.invocation.InterceptedInvocation["location"])
....
....
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.mockito.internal.debugging.LocationImpl and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: spring.boot.usingSpringBoot.entity.Reservation$MockitoMock$980801978["mockitoInterceptor"]->org.mockito.internal.creation.bytebuddy.MockMethodInterceptor["mockHandler"]->org.mockito.internal.handler.InvocationNotifierHandler["invocationContainer"]->org.mockito.internal.stubbing.InvocationContainerImpl["invocationForStubbing"]->org.mockito.internal.invocation.InvocationMatcher["invocation"]->org.mockito.internal.invocation.InterceptedInvocation["location"])
How can I inject reservation in the right way??
Thank you

You're getting the error because when you use #MockBean (or #Mock in a non Spring environment) you get a Mockito mock object. This object is a hollow proxy of your object.The proxy has the same public methods as your class and by default return the default value of it's return type (e.g. null for objects, 1 for ints, etc.) or does nothing for void methods.
Jackson is complaining because it has to serialize this proxy that has no fields and Jackson does not know what to do.
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No
serializer found for class org.mockito.internal.debugging.LocationImpl
and no properties discovered to create BeanSerializer (to avoid
exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS
In general when you mock a dependency of some class that you want to test, you mock it's public methods, that are used in the class that you test.
Directly returning your dependency is not a good real world use case - it's very unlikely that you will have to write a code like this.
I guess you're trying to learn so let me provide an improved example:
#RestController
public class ReservationController {
#Autowired
private ReservationService reservationService; //my chnage here
#RequestMapping(value = "/reservation", produces = MediaType.APPLICATION_JSON_UTF8_VALUE, method = RequestMethod.POST)
#ResponseBody
public Reservation getReservation() {
return reservationService.getReservation(); //my chnage here
}
}
Instead of directly injecting a value object you usually have a service class that contain some business logic and return something - in my example ReservationService which have a method getReservation() that return and object of type Reservation.
Having that, in your test you can mock the ReservationService.
#WebMvcTest
#RunWith(SpringRunner.class)
public class MvcTest {
#Autowired
private MockMvc mockMvc;
#MockBean(name = "reservation")
private ReservationService reservationService; //my chnage here
#Test
public void postReservation() throws Exception {
// You need that to specify what should your mock return when getReservation() is called. Without it you will get null
when(reservationService.getReservation()).thenReturn(new Reservation()); //my chnage here
mockMvc.perform(MockMvcRequestBuilders.post("/reservation"))
.andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE))
.andExpect(MockMvcResultMatchers.status().isOk());
}
}

Related

Spring Boot Service Junit Mockito issue

I have written Junit for the service. mocking dao.
Service method return type in EmployeeDTO.
Dao return type is Employee.
problem is employee to employeeDto conversion failed in test case.
when(dao.method()).thenReturn(new Employee), so on call od service.method() I am facing issue since dozer is in between to convert employee to employeedto in the actual code.
any suggestions to fix this.
#SpringBootTest(classes = { EmployeeSearchService.class, EmployeeDao.class })
public class EmployeeSearchServiceTest {
#Mock // will create a mock implementation for the EmployeeDao
EmployeeDao employeeDao;
#InjectMocks // will inject the mocks marked with #Mock to this instance when it is created.
private EmployeeSearchService employeeSearchService ;
#Mock
private DozerBeanMapper dozerBeanMapper;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#DisplayName("fetchEmployees with valid data")
#Test
public void testfetchEmployeesWithValidData() throws IOException {
when(employeeDao.fetchEmployees()).thenReturn(Stream
.of(new Employee((long) 1, "James", "Java", "Manager", "Google"),
new Employee((long) 2, "Richard", "C++", "Manager", "Microsfot"))
.collect(Collectors.toList()));
//when(dozer.map(Mockito.any(), Mockito.any())).thenReturn(employeeDTO);
System.out.println(employeeSearchService.fetchEmployees());
assertEquals(4, employeeSearchService.fetchEmployees().size());
}
}
#Service
public class EmployeeSearchServiceImpl implements EmployeeSearchService {
#Autowired
EmployeeDao employeeDao;
#Autowired
DozerBeanMapper dozerBeanMapper;
#Override
#Logging
public List<EmployeeDTO> fetchEmployees() throws IOException {
List<Employee> aEmployeeList = employeeDao.fetchEmployees();
List<EmployeeDTO> aEmployeeDTOList= aEmployeeList.stream().map(emp ->
dozerBeanMapper.map(emp,
EmployeeDTO.class)).collect(Collectors.toList());
if (aEmployeeList.isEmpty()) {
throw new EmployeeNotfoundException("Employee Details Not Available");
}
return aEmployeeDTOList;
}
}
#Repository
public class EmployeeDaoImpl implements EmployeeDao {
#Override
#Logging
public List<Employee> fetchEmployees() throws IOException {
List<String> aFileList=fileUtils.getFileContent(EmployeeSearchConstants.EMPLOYEE_DETAILS_PATH);
List<Employee> aEmployeList = getEmployee(aFileList);
if (aEmployeList.isEmpty()) {
throw new EmployeeNotfoundException("Employee Details Not Available");
}
return aEmployeList;
}
}
If I understand you correctly your issue is that you are looking for a way to convert your Employee into a EmployeeDTO object, which in your code is done using dozerBeanMapper.map(emp, EmployeeDTO.class).
One option would be to change the EmployeeSearchServiceImpl and use Constructor Injection instead of Field Injection. This way you could simply use the real dozer class to do the mapping (by manually passing the mock for employeeDao and the real dozerBeanMapper).
Constructor Injection is done by moving the #Autowired to the constructor instead of the fields. Depening on your spring version and in case you only have one constructor for the class you might be able to omit the annotation. For more information check here.
EmployeeDao employeeDao;
DozerBeanMapper dozerBeanMapper;
#Autowired
public EmployeeSearchServiceImpl(EmployeeDao employeeDao, DozerBeanMapper dozerBeanMapper) {
this.employeeDao = employeeDao;
this.dozerBeanMapper = dozerBeanMapper;
}
Another option would be to use Mockito's thenAnser functionality. However
you still require something to do the conversion for you.
when(dozerBeanMapper.map(Mockito.any(), Mockito.any())).thenAnswer(new Answer<EmployeeDTO>() {
public EmployeeDTO answer(InvocationOnMock invocation) {
Employee employee = (Employee) invocation.getArguments()[0];
// convert to EmployeeDTO
EmployeeDTO dto = ...
return dto;
}
});

#CachePut does not work in #Configuration for pre cache

I was trying to use spring stater-cache in spring boot 1.3.5, everything works fine except pre load cache in #Configuration class.
Failed tests:
CacheTest.testCacheFromConfig: expected:<n[eal]> but was:<n[ot cached]>
Please take a look at the code as below, if you met this before, please share it with me :)
#Component
public class CacheObject{
#CachePut(value = "nameCache", key = "#userId")
public String setName(long userId, String name) {
return name;
}
#Cacheable(value = "nameCache", key = "#userId")
public String getName(long userId) {
return "not cached";
}
}
#Component
public class CacheReference {
#Autowired
private CacheObject cacheObject;
public String getNameOut(long userId){
return cacheObject.getName(userId);
}
}
#Configuration
public class SystemConfig {
#Autowired
private CacheObject cacheObject;
#PostConstruct
public void init(){
System.out.println("------------------");
System.out.println("-- PRE LOAD CACHE BUT DIDN'T GET CACHED");
System.out.println("------------------");
cacheObject.setName(2, "neal");
cacheObject.setName(3, "dora");
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = BootElastic.class)
#WebAppConfiguration
public class CacheTest {
#Autowired
private CacheObject cacheObject;
#Autowired
private CacheReference cacheReference;
#Test
public void testCache(){
String name = "this is neal for cache test";
long userId = 1;
cacheObject.setName(userId, name);
// cacheObject.setName(2, "neal"); // this will make test success
String nameFromCache = cacheReference.getNameOut(userId);
System.out.println("1" + nameFromCache);
Assert.assertEquals(nameFromCache, name);
}
#Test
public void testCacheFromConfig(){
String nameFromCache = cacheReference.getNameOut(2);
System.out.println("4" + nameFromCache);
Assert.assertEquals(nameFromCache, "neal");
}
}
#PostConstruct methods are called right after all postProcessBeforeInitialization() BeanPostProcessor methods invoked, and right before postProcessAfterInitialization() invoked. So it is called before there is any proxy around bean, including one, putting values to cache.
The same reason why you can't use #Transactional or #Async methods in #PostConstruct.
You may call it from some #EventListener on ContextRefreshedEvent to get it working

Using jUnit mock objects with Spring

I'm trying to write a unit test for a class that uses Spring. The code itself seems to be fine, but I keep getting a null pointer exception on my When statements for some reason. The source code is as follows:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"/spring-bean-config.xml"}
public class testClass {
#Mock TestPerson mockTestPerson;
private TestObject testObject;
#Before
public void setup() { testObject = new TestObject(); }
#Test
public void testGetFullName() throws Exception {
String firstname = "Bob";
String lastname = "Barker";
when(testPerson.getFirstName()).thenReturn(firstName); // Throws NullPointerException
when(testPerson.getLastName()).thenReturn(lastName); // I suspect this guy will do the same.
String result = testObject.getFullName(mockTestPerson);
assertNotNull(result);
}
}
The TestPerson class is pretty simple:
public class testPerson {
private String firstName;
private String lastName;
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName {
return this.LastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
And finally, the TestObject class.
public class TestObject {
public String getFullName(TestPerson testPerson) {
return testPerson.getFirstName + " " + testPerson.getLastName();
}
}
Simple, yes? To be honest, it might be easier to just initialize a TestPerson object from within the test. For argument's sake (and for the fact that my other projects that NEED to use #Mock tend to make the same complaints), I need to know how to properly mock object using the #Mock annotation AND SpringJUnit4ClassRunner.
EDIT:
So I tried creating a new TestPerson from directly within the test and setting the first and last name. Curiously, I still get a null pointer exception at the same line. Why is that? If I can't create or mock the object, then how do I verify the object is working?
Your test doesn't need Spring. You should remove the both annotations
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"/spring-bean-config.xml"}
The #Mock annotation is not doing anything by itself. You have to create the mocks. This can be done using a rule
#Rule
public MockitoRule rule = MockitoJUnit.rule();
or by an #Before method.
#Before
public void initMocks() {
MockitoAnnotations.initMocks(this);
}
See the Mockito documentation for details.

Testing #ModelAttribute method on a controller

This are the annotated methods in the controller:
#RequestMapping(method = RequestMethod.GET)
public String getClient(#PathVariable("contractUuid") UUID contractUuid, Model model) {
ClientDto clientDto = new ClientDto();
clientDto.setContractUuid(contractUuid);
model.addAttribute("client", clientDto);
return "addClient";
}
#ModelAttribute("contract")
public ContractDto getContract(#PathVariable("contractUuid") UUID contractUuid) throws ContractNotFoundException {
return contractService.fromEntity(contractService.findByUuid(contractUuid));
}
The test method that I am trying is shown below, but it fails for attribute contract. The attribute client is added to the Model in a #RequestMapping method.
private MockMvc mockMvc;
#Autowired
private ContractService contractServiceMock;
#Autowired
private ClientService clientServiceMock;
#Autowired
protected WebApplicationContext wac;
#Before
public void setup() {
Mockito.reset(contractServiceMock);
Mockito.reset(clientServiceMock);
this.mockMvc = webAppContextSetup(this.wac).build();
}
#Test
public void test() throws Exception {
UUID uuid = UUID.randomUUID();
Contract contract = new Contract(uuid);
when(contractServiceMock.findByUuid(uuid)).thenReturn(contract);
mockMvc.perform(get("/addClient/{contractUuid}", uuid))
.andExpect(status().isOk())
.andExpect(view().name("addClient"))
.andExpect(forwardedUrl("/WEB-INF/pages/addClient.jsp"))
.andExpect(model().attributeExists("client"))
.andExpect(model().attributeExists("contract"));
}
The contract attribute shows in the jsp page when I run the application, since I use some of its attributes, but since it fails in the test method is there another way to test it ?
It fails with the message:
java.lang.AssertionError: Model attribute 'contract' does not exist
Spring is 4.0.1.RELEASE
It seems it was my fault.
Even though the #ModelAttribute method returns an instance of ContractDto I only mocked one method used from the service:
when(contractServiceMock.findByUuid(uuid)).thenReturn(contract);
and so findByUuid returned something, but contractService.fromEntity was left untouched so I had to also mock it:
UUID uuid = UUID.randomUUID();
Contract contract = new Contract(uuid);
ContractDto contractDto = new ContractDto(uuid);
when(contractServiceMock.findByUuid(uuid)).thenReturn(contract);
when(contractServiceMock.fromEntity(contract)).thenReturn(contractDto);

Creating an aspect on a spring #Controller with #Autowired final fields in constructor

I have an aspect setup
#Aspect
#Component
public class JsonAspect {
#Around("execution(public au.com.mycompany.common.json.response.JsonResponse *(..)) " +
"&& #annotation(org.springframework.web.bind.annotation.RequestMapping)")
public final Object beforeMethod(final ProceedingJoinPoint joinPoint) throws JsonException {
try {
System.out.println("before...................");
System.out.println(joinPoint.getSignature().getName());
return joinPoint.proceed();
} catch (Throwable t) {
throw new JsonException(t);
}
}
}
I this should apply to a #Controller class with the following method
#RequestMapping(value = "/validate",
method = RequestMethod.POST,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public final JsonResponse<JsonValidationResponse> validateViaJson(...
The problem is that I am injecting dependencies via #Autowired
private final ClientService clientService;
private final VehicleService vehicleService;
#Autowired
public QuoteControllerImpl(
final ClientService clientService,
final VehicleService vehicleService,
) {
this.clientService = clientService;
this.vehicleService = vehicleService;
}
When I try to proxy this class it complains that there is no default constructor. so I decided to crate an interface for the class but now I get the following error on an unrelated method in the same class.
java.lang.IllegalArgumentException: object is not an instance of
declaring class
The above error applies to a method that is is in the same class but not part of the aspectj pointcut. If remove the aspectj pointcut it works (event with the new interface). So it seems that aspectj proxy is causing a problem somehow.
Anyone know why?
UPDATE
#nicholas.hauschild I tried your solution but now I am getting a NullPointer Exception when I initialise my map.
#ModelAttribute
public final void initialiseModel(final ModelMap map, #PathVariable("status") final String status) {
map.addAttribute(CLIENTS, clientService.getClients());
clientService is null.
I am not a huge fan of this solution, but if you create the default constructor along with the #Autowired one, Spring will use the #Autowiredone anyways.
private final ClientService clientService;
private final VehicleService vehicleService;
#Autowired
public QuoteControllerImpl(
final ClientService clientService,
final VehicleService vehicleService,
) {
this.clientService = clientService;
this.vehicleService = vehicleService;
}
public QuoteControllerImpl() {
//Spring won't use me...
this.clientService = null;
this.vehicleService = null;
}

Resources