Unit Testing
Introduction
In Unit Testing, the specific unit of the module is focused and tested with an assumption that the collaborations with other backends should work fine. For this reason, it is very useful if Mock Objects are provided to take place of the real collaboration with the backends.
CRISP API supports Mock Objects to make it easy to test your code which depends on CRISP API.
Mock Objects Support
The following Mock Objects and helper classes are provided:
- org.onehippo.cms7.crisp.mock.resource.MockJacksonResourceResolverAdapter
- Adapter class from which you can extend and create a Mock ResourceResolver instance for backends producing JSON payloads.
- org.onehippo.cms7.crisp.mock.resource.MockJdomResourceResolverAdapter
- Adapter class from which you can extend and create a Mock ResourceResolver instance for backends producing XML payloads.
- org.onehippo.cms7.crisp.mock.broker.MockResourceServiceBroker
- Mock ResourceServiceBroker class.
- org.onehippo.cms7.crisp.mock.module.MockCrispHstServices
- Helper to replace the system default ResourceServiceBroker in the unit test.
Unit Test Example with Mock ResourceResolver
Suppose you have a backend producing JSON payloads and your code depends on CRISP API invocations to communicate with the backend.
We are going to use MockJacksonResourceResolverAdapter to mock the ResourceResolver collabrating with the backend, register the Mock ResourceResolver instance into a MockResourceServiceBroker, and finally register the MockResourceServiceBroker instance as the system default one. So, your code will work fine with these Mock CRISP APIs.
Here's an example:
public class ProductServiceTest { @Test public void testFindProducts() throws Exception { Map<String, ResourceResolver> resourceResolverMap = new HashMap<>(); // 1. Create a mocking resourceResolver for the resourceSpace, "products", // and put it in the map. resourceResolverMap.put("products", new MockJacksonResourceResolverAdapter() { @Override public Resource findResources(String baseAbsPath, Map<String, Object> pathVariables, ExchangeHint exchangeHint) throws ResourceException { // 2. For this unit test purpose, simply read data from local file in the classpath // through this mock ResourceResolver whenever your code invokes #findResources(...). return urlToResource(ProductServiceTest.class.getResource("products-output.json")); } }); // 3. Create and register the Mock ResourceResolver and ResourceServiceBroker. ResourceServiceBroker mockBroker = new MockResourceServiceBroker(resourceResolverMap); MockCrispHstServices.setDefaultResourceServiceBroker(mockBroker); // 4. Now, you are all set and ready to invoke your code from here. ProductService productService = new ProductService(); // Suppose #getProducts() retrieves a ResourceServiceBroker through CrispHstServices.getDefaultResourceServiceBroker(...). // Then it will get the mockBroker we have set up above. // And, if #getProducts() invokes CrispHstServices.getDefaultResourceServiceBroker(...).findResources("products", ...), // then it will get the Resource object returned by the #urlToResource(...) call shown above. List<Product> products = productService.getProducts(); // 5. Assert somethings here. // ... } }
In summary, the steps are as follows:
- Create a Mock ResourceResolver by extending either MockJacksonResourceResolverAdapter or MockJdomResourceResolverAdapter, depending on the payload format from your backend.
- Create a Mock ResourceServiceBroker through MockResourceServiceBroker and put all ResourceResolvers in the map constructor argument.
- Register the Mock ResourceServiceBroker through MockCrispHstServices.setDefaultResourceServiceBroker(...) as the system default, so that any CRISP API invocations afterward will use the Mock Objects.
- Invoke your code that you want to test.
- Validate the status through assertions.
How to Register Real ResourceResolver?
Sometimes you might want to test code with real collaborations with the backends by setting a real ResourceResolver instance. As JUnit supports conditional testing with Assumptions, you could take advantage of it in some cases.
MockResourceServiceBroker accepts a map of pairs of resourceSpace key and ResourceResolver value, so you can add any ResourceResolver instance for a specific resourceSpace.
For example, the Simple JSON API Example page contains the following bean definitions for its ResourceResolver:
<bean parent="abstractCrispSimpleJacksonRestTemplateResourceResolver" class="org.onehippo.cms7.crisp.core.resource.jackson.SimpleJacksonRestTemplateResourceResolver"> <property name="cacheEnabled" value="${cache.enabled}" /> <property name="baseUri" value="${example.commerce.api.base.uri}" /> <property name="restTemplate"> <bean class="org.springframework.web.client.RestTemplate"> <property name="requestFactory" ref="org.springframework.http.client.ClientHttpRequestFactory" /> </bean> </property> <!-- ... --> </bean>
You can convert it into Java code in unit tests like the following:
public class MyExampleTest { @Test public void testFindAll() throws Exception { Map<String, ResourceResolver> resourceResolverMap = new HashMap<>(); // Java code example converted from the bean definition. SimpleJacksonRestTemplateResourceResolver resourceResolver = new SimpleJacksonRestTemplateResourceResolver(); resourceResolver.setCacheEnabled(true); resourceResolver.setBaseUri("https://commerce.example.com/api"); resourceResolver.setRestTemplate(new RestTemplate(new SimpleClientHttpRequestFactory())); // Register the resourceResolver with resourceSpace, 'simpleSpace', as an exmaple. resourceResolverMap.put("simpleSpace", resourceResolver); ResourceServiceBroker mockBroker = new MockResourceServiceBroker(resourceResolverMap); MockCrispHstServices.setDefaultResourceServiceBroker(mockBroker); // ... } }
Or if you are familiar with creating beans directly in unit tests through a Spring Framework BeanFactory such as org.springframework.context.support.ClassPathXmlApplicationContext, then you can load and use the ResourceResolver bean through a BeanFactory, too.