Search code examples
javaspring-bootjunitmockitospring-restcontroller

Why does @InjectMocks controller's response get turned into empty by Mockito when I use @JsonView?


I am getting an empty json {} (verified via actions.andReturn().getResponse().getContentAsString()) as a response when I test via mockMvc.perform(post...)) despite the method actually returning a response (I see this when I debug and step through the code. It's a valid, filled response object but suddenly becomes null when some code in mockito is making a modalAndView -why does it do this?).

Test class:

@RunWith(MockitoJUnitRunner.class)
public class Test
{
    //Also tried @Mock
    @InjectMocks
    private MyService myService;

    @Mock
    private MyDAO myDAO;

    //@Autowired
    @InjectMocks
    private MyController myController;

    @Before
    public void setup() {

        //Build the controller mock handler
        mockMvc = MockMvcBuilders
            .standaloneSetup(myController)
            .setControllerAdvice(new ExceptionHandler())
            .build();
    }

    @org.junit.Test
    public void testMyEndpoint() throws Exception
    {
        //Make a request object
        MyRequest request = readJson("request.json", MyRequest.class );

        List<MyObject> objects = readJson("data.json", MyObject.class );

        Mockito.when(
            myDAO.getData( request )
        ).thenReturn(objects);

        Mockito.when(
            myService.callDAO(request)
        )
            .thenReturn(objects)

        //Call the aum endpoint
        ResultActions actions = mockMvc.perform(
            post( "/v1/endpoint" )
                .contentType(MediaType.APPLICATION_JSON)
                .content( new ObjectMapper().writeValueAsString( request ) )
        );

        //Why is this empty?
        System.out.println( actions.andReturn().getResponse().getContentAsString() );
    }
}

Solution

  • Mockito uses an ObjectMapper that does not understand @JsonView. To get around this you need to set a message converter that does.

    @RunWith(MockitoJUnitRunner.class)
    public class Test
    {
        //Also tried @Mock
        @InjectMocks
        private MyService myService;
    
        @Mock
        private MyDAO myDAO;
    
        //@Autowired
        @InjectMocks
        private MyController myController;
    
        /**
         * This is required for JsonViews.
         * @return
         */
        public static MappingJackson2HttpMessageConverter createJacksonConverter() {
            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.configure(MapperFeature.DEFAULT_VIEW_INCLUSION, true);
    
            MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
            converter.setObjectMapper(objectMapper);
            return converter;
        }
    
        @Before
        public void setup() {
    
            //Build the controller mock handler
            mockMvc = MockMvcBuilders
                .standaloneSetup(myController)
                .setControllerAdvice(new ExceptionHandler())
                .setMessageConverters(createJacksonConverter())
                .build();
        }
    
        @org.junit.Test
        public void testMyEndpoint() throws Exception
        {
            //Make a request object
            MyRequest request = readJson("request.json", MyRequest.class );
    
            List<MyObject> objects = readJson("data.json", MyObject.class );
    
            Mockito.when(
                myDAO.getData( request )
            ).thenReturn(objects);
    
            Mockito.when(
                myService.callDAO(request)
            )
                .thenReturn(objects)
    
            //Call the aum endpoint
            ResultActions actions = mockMvc.perform(
                post( "/v1/endpoint" )
                    .contentType(MediaType.APPLICATION_JSON)
                    .content( new ObjectMapper().writeValueAsString( request ) )
            );
    
            //Why is this empty?
            System.out.println( actions.andReturn().getResponse().getContentAsString() );
        }
    }