JUnit: Unable to mock the RestTemplate object to call postForObject method

21.7k Views Asked by At

I am new to Mockito as well as Spring's RestTemplate. I am working on JUnit tests for a functionality which sends a request to a web-service and gets the response through the use of RestTemplate. I want the server to respond with a response that i want so that i can test the functionalities based on this response. I am using Mockito for mocking.

I am not sure where I am going wrong. Am I not creating proper mocks? Is my JSON object mapper not been configured properly?

Configuration file defining the RestTemplate bean:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

        <bean id="restTemplate" class="org.springframework.web.client.RestTemplate">
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
                <bean class="org.springframework.http.converter.xml.SourceHttpMessageConverter"/>
                <bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
                    <property name="marshaller"  ref="xsStreamMarshaller" />
                    <property name="unmarshaller" ref="xsStreamMarshaller" />
                </bean>
            </list>
        </property>
    </bean>    
    <bean id="xsStreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller"></bean>
</beans>

My DTO's:

import org.codehaus.jackson.annotate.JsonWriteNullProperties;

@JsonWriteNullProperties(false)
public abstract class BaseDTO {

    protected boolean error;

    public boolean isError() {
        return error;
    }

    public void setError(boolean error) {
        this.error = error;
    }

}

public class ChildDTO extends CommercialBaseDTO {   

    private String fullName;    

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }
}

Class containing the method to test:

package com.exmpale.mypackage;  

import org.springframework.web.client.RestTemplate;

@Component
public class MyUtilClass {

    @Autowired
    private RestTemplate restTemplate;

    public RestTemplate getRestTemplate(){
        return restTemplate;
    }

    public void setRestTemplate(RestTemplate restTemplate){
        this.restTemplate = restTemplate;
    }

    // Method to test       
    public ChildDTO getChildDTO(MyUser myUser, HttpServletRequest request, HttpServletResponse response)
    {
        response.setContentType("application/json");        

        //Nothing much here, it takes the myUser and convert into childDTO  
        ChildDTO childDTO = new MyUtilClass().getDTOFromUser(request, myUser);  

        //This is the restTemplate that iam trying to mock.
        childDTO = restTemplate.postForObject("http://www.google.com", childDTO, ChildDTO.class);

        if (childDTO.isError()) {
            //Then do some stuff.........           
        }
        return childDTO;
    }
}

The JUnit test class

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"test-config.xml"})
public class MyUtilClassTest {

    @InjectMocks
    RestTemplate restTemplate= new RestTemplate();

    private MockRestServiceServer mockServer;

    @Before
    public void setUp() throws Exception {

        MockitoAnnotations.initMocks(this);

        //Creating the mock server      

        //Add message conveters
        List<HttpMessageConverter<?>> messageConverters = new ArrayList<HttpMessageConverter<?>>();
        messageConverters.add(new FormHttpMessageConverter());
        messageConverters.add(new StringHttpMessageConverter());
        messageConverters.add(new MappingJacksonHttpMessageConverter());

        //Create Object mapper
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure( DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure( SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false);
        objectMapper.configure( SerializationConfig.Feature.AUTO_DETECT_FIELDS, true);
        objectMapper.configure( SerializationConfig.Feature.AUTO_DETECT_GETTERS,true);
        objectMapper.configure( SerializationConfig.Feature.AUTO_DETECT_IS_GETTERS,true);
        MappingJacksonHttpMessageConverter jsonMessageConverter = new MappingJacksonHttpMessageConverter();
        jsonMessageConverter.setObjectMapper(objectMapper);
        messageConverters.add(jsonMessageConverter);

        //Set the message converters 
        restTemplate.setMessageConverters(messageConverters);        
        mockServer = MockRestServiceServer.createServer(restTemplate);
    }

    @Test
    public void testGetChildDTO()throws Exception {

        MyUtilClass myUtil = new MyUtilClass();         
        MyUser myUser = new MyUser();

        HttpServletRequest request = new HttpServletRequestWrapper(new MockHttpServletRequest());
        HttpServletResponse response = new HttpServletResponseWrapper(new MockHttpServletResponse());       

        //create the mocks for ChildDTO. I want MyUtilClass().getDTOFromUser(request, myUser) to return this.
        ChildDTO childDTOMock_One =  Mockito.mock(ChildDTO);            

        //Want this to be returned when restTemplate.postForObject() is called.
        ChildDTO childDTOMock_Two =  Mockito.mock(ChildDTO.class);
        childDTOMock_Two.setError(false);

        //create the mocks for userMgntUtils
        MyUtilClass myUtilClassMock =  Mockito.mock(MyUtilClass.class);     

        //stub the method getDTOFromUser() to return the mock object. I need this mock to be passed to 'postForObject()'
        Mockito.when(myUtilClassMock.getDTOFromUser(request, myUser)).thenReturn(childDTOMock_One);

        String responseJSON="{\"error\":false}";

        //set the expectation values for mockServer
        mockServer.expect( requestTo("http://www.google.com")).andExpect(method(HttpMethod.POST)).andRespond(withSuccess(responseJSON,MediaType.APPLICATION_JSON));

        //set the expectation values for restTemplate
        Mockito.when(restTemplate.postForObject( "http://www.google.com", childDTOMock_One, ChildDTO.class)).thenReturn(childDTOMock_Two);

        TypedUserDTO result = userMgmtUtils.getUserProfileDTO(registerUser, request, response, action);
        assertNotNull(result);
    }
}

Getting the following exception:

org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: No serializer found for class org.mockito.internal.stubbing.defaultanswers.GloballyConfiguredAnswer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: com.biogenidec.dto.TypedUserDTO$$EnhancerByMockitoWithCGLIB$$bee3c447["callbacks"]->org.mockito.internal.creation.MethodInterceptorFilter["handler"]->org.mockito.internal.handler.InvocationNotifierHandler["mockSettings"]->org.mockito.internal.creation.settings.CreationSettings["defaultAnswer"]); nested exception is org.codehaus.jackson.map.JsonMappingException: No serializer found for class org.mockito.internal.stubbing.defaultanswers.GloballyConfiguredAnswer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: com.biogenidec.dto.TypedUserDTO$$EnhancerByMockitoWithCGLIB$$bee3c447["callbacks"]->org.mockito.internal.creation.MethodInterceptorFilter["handler"]->org.mockito.internal.handler.InvocationNotifierHandler["mockSettings"]->org.mockito.internal.creation.settings.CreationSettings["defaultAnswer"])

And:

Caused by: org.codehaus.jackson.map.JsonMappingException: No serializer found for class org.mockito.internal.stubbing.defaultanswers.GloballyConfiguredAnswer and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: com.biogenidec.dto.TypedUserDTO$$EnhancerByMockitoWithCGLIB$$bee3c447["callbacks"]->org.mockito.internal.creation.MethodInterceptorFilter["handler"]->org.mockito.internal.handler.InvocationNotifierHandler["mockSettings"]->org.mockito.internal.creation.settings.CreationSettings["defaultAnswer"])
2

There are 2 best solutions below

2
On

The idea of Mockito is to test the class and none of the dependencies outside of it. So if your testing MyUtilClass you want to mock the RestTemplate class. And your @InjectMocks is on the wrong class see below.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"test-config.xml"})
public class MyUtilClassTest 
{
    @Mock
    private RestTemplate restTemplate;
    @InjectMocks
    private MyUtilClass myUtilClass;

    @Before
    public void setUp() throws Exception 
    {

        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testGetChildDTO()throws Exception 
    {

         MyUser myUser = new MyUser();
         HttpServletRequest request = new HttpServletRequestWrapper(new MockHttpServletRequest());
         HttpServletResponse response = new HttpServletResponseWrapper(new MockHttpServletResponse());    

         Mockito.when(RestTemplate.postForObject(Mockito.eq("http://www.google.com", 
            Mockito.any(ChildDTO.class), Mockito.eq(ChildDTO.class)))).thenAnswer(
            new Answer<ChildDTO>()
            {
                @Override
                public ChildDTO answer(InvocationOnMock invocation) throws Throwable
                {
                     //The below statement takes the second argument passed into the method and returns it
                    return (ChildDTO) invocation.getArguments()[1];
                }
            });

         ChildDTO childDTO = myUtilClass.getDTOFromUser(request, myUser);

         //then verify that the restTemplate.postForObject mock was called with the correct parameters
         Mockito.verify(restTemplate, Mockito.times(1)).postForObject(Mockito.eq("http://www.google.com",
            Mockito.eq(childDTO), Mockito.eq(ChildDTO.class));
    }
 }

Also I find it bad practice to test other frameworks classes, more often then not they already tested their class and your just duplicating their work.

0
On

As correctly noted above, to test your method with mockito, it is not necessary to initialize restTemplate. It is enough to verify that the parameters of the input are correct (if needed) and return the correct mocking object from restTemplate.

We do not test the restTemplate here, we only test our code. This is the purpose of unit tests.

You can do something like this, or something simpler:

@RunWith(value = MockitoJUnitRunner.class)
public class Test {

@InjectMocks
private MyUtilClass testObj;

@Mock
private RestTemplate restTemplate;
@Mock
MyUser myUser;
@Mock
HttpServletRequest request;
@Mock
HttpServletResponse response;

@Test
public void test() throws Exception {
        //Configure sample to comparison and verification the result of the method:
        ChildDTO sample = getSample();

        //configure mocks:
        ChildDTO myObject = new ChildDTO();
        //configure myObject properties
        ResponseEntity<ChildDTO> respEntity = new ResponseEntity<>(
                myObject, HttpStatus.ACCEPTED);

        when(restTemplate.postForObject(anyString(), Matchers.<HttpEntity<?>>any(),
                Matchers.any(Class.class))).thenReturn(respEntity);
        //other stuff to configure correct behaviour of mocks request, response e.t.c.

        //act:
        ChildDTO result = testObj.getChildDTO(myUser, request, response);

        //verify that correct parameters were passed into restTemplate method "postForObject":
        verify(restTemplate).postForObject(eq("http://www.google.com"), Matchers.<HttpEntity<?>>any(),
                eq(ChildDTO.class)).thenReturn(respEntity);
        //assert to verify that we got correct result:
        assertEquals(sample, result);
    }    
}