Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unit Test using the DomainClassConverter feature of Spring

I'm trying to code a unit test for a method defined in a controller. The method is like this:

@RestController
@RequestMapping("/products")
public class RestProductController {

 @RequestMapping(value="/{product}/skus", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
    public List<SkuSummaryVO> getSkuByProduct(@Valid @PathVariable Product product){
        List<SkuSummaryVO> skusByProductVOs = skuService.getSkusByProduct(product);
        return skusByProductVOs;
    }
}

We use in our Configuration class the annotation @EnableSpringDataWebSupport to enable the DomainClassConverter feature. So we can use the JPA entity as a @PathVariable. So when a product id will be set in the URL, we will get the product (with a request behind the scene).

We are developing unit test without enabling the Spring App Context and using Mockito. So we initialize the mockMvcBuilders like this:

public class RestProductControllerTest {

    ...

    @Before
    public void setUp() {
        RestProductController restProductController = new RestProductController();
        ...
        mockMvc = MockMvcBuilders.standaloneSetup(restProductController).build();
    }
}

and the test method is like this:

    @Test
    public void testGetProductById() throws Exception {
        ...
        String jsonResult = ...;     
        mockMvc.perform(get("/products/123/skus").contentType(MediaType.APPLICATION_JSON)).andExpect(status().isOk()).andExpect(content().string(jsonResult));
    }

And I get a 500 for the HttpCode (the status)

And the unit tests work fine for the controller methods witch are not using the DomainClassConverter feature (for example if I use a Long productId instead of a Product product as a parameter of getSkuByProduct, it will work)

like image 617
Emilien Brigand Avatar asked Nov 01 '22 15:11

Emilien Brigand


1 Answers

UPDATE: on second thought, what I originally proposed below could never work since DomainClassConverter requires that your Spring Data Repositories be present in the ApplicationContext, and in your example you are using StandaloneMockMvcBuilder which will never create an ApplicationContext that contains your Spring Data Repositories.

The way I see it, you have two options.

  1. Convert your test to an integration test using a real ApplicationContext (loaded via @ContextConfiguration as demonstrated in the reference manual) and pass that to MockMvcBuilders.webAppContextSetup(WebApplicationContext). If the configured ApplicationContext includes your Spring Data web configuration, you should be good to go.
  2. Forgo the use of DomainClassConverter in your unit test, and instead set a custom HandlerMethodArgumentResolver (e.g., a stub or a mock) via StandaloneMockMvcBuilder.setCustomArgumentResolvers(HandlerMethodArgumentResolver...). Your custom resolver could then return whatever Product instance you desire.

You'll have to register an instance of the DomainClassConverter with the ConversionService in the StandaloneMockMvcBuilder that is created when you invoke MockMvcBuilders.standaloneSetup(Object...).

In SpringDataWebConfiguration.registerDomainClassConverterFor(), you can see that the DomainClassConverter is instantiated (and indirectly registered) like this:

DomainClassConverter<FormattingConversionService> converter =
    new DomainClassConverter<FormattingConversionService>(conversionService);
converter.setApplicationContext(context);

And you can set your own FormattingConversionService via StandaloneMockMvcBuilder.setConversionService(). See WebMvcConfigurationSupport.mvcConversionService() for an example of how to configure the ConversionService for a web environment.

The challenge then is how to obtain a reference to the ApplicationContext. Internally, StandaloneMockMvcBuilder uses a StubWebApplicationContext, but as far as I can see (prior to Spring 4.1) there is no way to access it directly without subclassing StandaloneMockMvcBuilder.

As of Spring Framework 4.1, you could implement a custom MockMvcConfigurer (which gives you access to the WebApplicationContext via its beforeMockMvcCreated() method.

So hopefully that's enough information to get you on the right track!

Good luck...

Sam

like image 195
Sam Brannen Avatar answered Nov 08 '22 05:11

Sam Brannen