loading table of contents...

4.3.8. Unit Testing a CAE Application

In order to promote a test-driven approach for development and to make testing of services implemented with the CAE application framework easier, CoreMedia ships an ease to use test add-on to be used in your tests based on Spring Testing.

Differing from the unit testing approach, it doesn't focus on testing single classes only but helps to test services in a larger context and therefore brings the tests closer to the real world.

This approach enables to develop system tests at unit test level as there is no need for running external systems such as a content server or a servlet engine. The basic idea is to use a Spring application context that is composed from the same Spring bean declaration files that are used in the project.

[Note]Note

Note that this requires the project Spring bean declaration in general to be self-contained and independent from each other. Otherwise, the application context could become too unhandy for testing when too many declarations have to be included recursively.

The add-on provided by CoreMedia supports an easy and convenient setup of an application context providing especially an in-memory content repository for your tests.

Below you will find two examples. For more examples, usage information and templates you might want to use in your IDE have a look at XmlRepoConfiguration.

Examples

Testing Link Schemes

This example demonstrates how to set up an infrastructure that can be used for testing project link schemes. In the project's bean declaration myproject-linkschemes-beans.xml several link schemes are defined, as well as some CAE basic infrastructure such as the LinkFormatter bean. It is very useful to load exactly this file into a test application context, in order to...

  1. test the contents of the file itself, for example detect whether there a syntactical or wiring problems

  2. test the service instances with a configuration that is (nearly) equal to the configuration used in the project

  3. test the service (in this example: the links scheme) in interaction with similar services, for example make sure that a certain link scheme is addressed for certain parameters and not a different link scheme instance.

Use the configuration pattern to construct the application context with the desired configuration:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MyTest.LocalConfig.class)
@ActiveProfiles(MyTest.LocalConfig.PROFILE)
public class MyTest {
  @Configuration
  @ImportResource(
          value = {
                  XmlRepoResources.LINK_FORMATTER,
                  "classpath:/com/mycompany" +
                  "/myproject/myproject-linkschemes-beans.xml"
          },
          reader = ResourceAwareXmlBeanDefinitionReader.class
  )
  @Import(XmlRepoConfiguration.class)
  @Profile(PROFILE)
  public static class LocalConfig {
    public static final String PROFILE = "MyTest";
  }

  // ...
}

Using a local test-only profile is recommended if you are using component scan to find your beans. If not using the ActiveProfile, Profile annotation pair LocalConfig classes of other tests might be found through component scan.

Now you can just inject the LinkFormatter and use it as in production code:

@Inject
LinkFormatter linkFormatter;

String link = linkFormatter.formatLink(
        new MyPage(123),
        "myView",
        new MockHttpServletRequest(),
        new MockHttpServletResponse(),
        false);

Assert.assertEquals("/123?view=myView", link);
Testing Handlers

A controller/handler's behavior strongly depends on the concrete setup of the application context. For instance, the registered Converters or PropertyEditors might have an influence on its behavior as well as the currently used HandlerMapping. Thus, it might be useful to take this environment into account when testing a handler. Spring provides MockMvc for emulating servlet requests and by capturing a handler's ModelAndView result. See corresponding JavaDoc for details.

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(classes = MyTest.LocalConfig.class)
@ActiveProfiles(MyTest.LocalConfig.PROFILE)
public class MyTest {
  @Configuration
  @ImportResource(
          value = {
                  XmlRepoResources.HANDLERS,
                  "classpath:/com/mycompany" +
                  "/myproject/myproject-handlers-beans.xml"
          },
          reader = ResourceAwareXmlBeanDefinitionReader.class
  )
  @Import(XmlRepoConfiguration.class)
  @Profile(LocalConfig.PROFILE)
  public static class LocalConfig {
    public static final String PROFILE = "MyTest";

    @Bean
    @Scope(SCOPE_SINGLETON)
    MockMvc mockMvc(WebApplicationContext wac) {
      return MockMvcBuilders.webAppContextSetup(wac).build();
    }
  }

  @Inject
  private MockMvc mockMvc;

  @Test
  public void test() throws Exception {
    Object expectedModelBean =...;
    mockMvc
       .perform(
            MockMvcRequestBuilders
                    .get("/context/servlet/123")
                    .servletPath("/servlet")
                    .contextPath("/context")
       )
       .andExpect(MockMvcResultMatchers.status().isOk())
       .andExpect(MockMvcResultMatchers
                    .model()
                    .attribute(
                               HandlerHelper.MODEL_ROOT,
                               Matchers.equalTo(expectedModelBean)
                    )
       );
  }
}

Mind the test annotation @WebAppConfiguration which is required to have a WebApplicationContext available to build the MockMvc object.

MockMvcResultMatchers provides several matchers for validating the response. For more sophisticated analysis you can end the validation with andReturn() and get for example the ModelAndView from the returned MvcResult.

More information

Take a look at the Javadoc of XmlRepoConfiguration for getting more examples and how to use for example a custom in memory content repository.