简体   繁体   中英

How to mock Spring ConversionService?

The web application I wrote works as expected. Now I want to unit-test the Controller methods. The pattern for these methods is:

  1. Convert the http request object (DTO) to domain object
  2. Use domain object to call business logic in a service layer
  3. Convert the response of the business logic to a response (DTO) object

For the conversion steps I use the Spring ConversionService, a bean configured as follows:

<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
  <list>
    <bean class="my.package.RequestToDomainConverter" />
    <bean class="my.package.DomainToResponseConverter" />
  </list>
</property>

This bean is Autowired into my controller:

@Autowired
ConversionService conversionService;

and used like this:

@RequestMapping(method  =  RequestMethod.POST,
                headers = "Accept=application/json")
@ResponseStatus(value = HttpStatus.CREATED) 
@ResponseBody 
public ResponseDTO createSelection( 
    @RequestBody RequestDTO requestDTO,
    HttpServletResponse response,
    Authentication authentication ) {

    DomainObject domainObject = conversionService.convert(requestDTO, DomainObject.class);
    // note: during test the conversionService returns null here...
    DomainObject businessAnswer = BusinessLayer.doService(domainObject);
    ResponseDTO responseDTO = conversionService.convert(businessAnswer, ResponseDTO.class);
return responseDTO;
}

As stated above, when deployed to an application server the application works as expected.

My test class is built as follows:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations={"classpath:/my/package/MyControllerTest-context.xml"})
public class MyControllerTest {
    private MockMvc mockMvc;

    @Mock
    private ConversionService conversionService;

    @Mock
    private BusinessLayer businessLayer;

    @InjectMocks
    private MyController myController;

    @Before
    public void setup() throws Exception {
        MockitoAnnotations.initMocks(this);
        mockMvc = MockMvcBuilders.standaloneSetum(myController).build();
    }

    @Test
    public void testCreateSelection(){
        // create a json string representation of the requestDTO
        String jsonDTO = new String("a valid json representation");

        // create objects to convert
        RequestDTO myRequestDTO = new RequestDTO();
        DomainObject myDomainObject = new DomainObject();
        ResponseDTO responseDTO = new ResponseDTO();

        // instruct the conversionservice mock to return the expected objects
        when(conversionService.convert(myRequestDTO, DomainObject.class))
            .thenReturn(myDomainObject);
        when(conversionService.convert(domainResponse, ResponseDTO.class))
            .thenReturn(myResponseDTO);

        // the businessLayer mock returns the same object that was given to it
        when(businessLayer.doService(domainObject))
            .thenReturn(domainObject);

        //create the necessary http headers
        HttHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("Accept", "application/json");

        //execute the controller method
        mockMvc.perform(post("/selection")
            .content(jsonDTO)
            .contentType(MediaType.APPLICATION_JSON)
            .headers(httpHeaders))
        .andExpect(status().isOk());
        // further testing...
    }
}

When running this test in debug mode I see that the createSelection method in my controller is successfully called, and while in the method the requestDTO object has the values that were given to the jsonDTO object.

However, the conversionService returns null when asked to convert the requestDTO to a DomainObject.

Why is this, and how to set up my test to have it return the converted object?

It is because in the line below:

when(conversionService.convert(myRequestDTO, DomainObject.class))
        .thenReturn(myDomainObject);

Method expect to receive the same object myRequestDTO which is your case is different because inside of your controller you will create another instance of the same class. Both have been created from the same class but have different identity. Instead you can use:

when(conversionService.convert(any(DomainObject.class), Matchers.<DomainObject>any()))
        .thenReturn(myDomainObject);

This allow to expect the same object, it doesn't matter the identity of this objects

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM