Testing MockBean Null

15,925

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());
    }
}
Share:
15,925
dargui
Author by

dargui

Updated on June 04, 2022

Comments

  • dargui
    dargui almost 2 years

    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