1

I want to write integration tests using springs MockMVC tools:

MockMvcBuilders.webAppContextSetup(...).build();

Reading the available documentation http://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#spring-mvc-test-framework leaves one question: How do I set up the test for a programmatically created WebApplicationContext?

If I correctly understood the idea of the mock mvc tests, it magically discovers a servlet context containing the dispatcher servlet and a WebApplicationContext for setup. Currently, I manually create the WebApplicationContext and add the DispatcherServlet to a jetty instance. I guess I have to somehow decouple that?

I want to use the web context defined in SpringWebConfiguration.class and mock everything in the root context defined in SpringConfiguration.class

public static void main(String[] args) throws Exception {
    SimpleCommandLinePropertySource ps = new SimpleCommandLinePropertySource(args);

    ApplicationContext rootContext = createRootContext(ps);

    //start jetty
    Server server = new Server(Integer.parseInt(ps.getProperty("port")));
    ServletContextHandler contextHandler = new ServletContextHandler();
    contextHandler.setContextPath("/");
    createWebContext(rootContext, contextHandler);
    server.setHandler(contextHandler);

    server.start();
    server.join();
}

private static ApplicationContext createRootContext(PropertySource propertySource) {
    AnnotationConfigApplicationContext rootContext = new AnnotationConfigApplicationContext();
    rootContext.getEnvironment().getPropertySources().addFirst(propertySource);
    rootContext.register(SpringConfiguration.class); //main configuration class for all beans
    rootContext.refresh();
    return rootContext;
}


public static WebApplicationContext createWebContext(ApplicationContext rootContext) {
    AnnotationConfigWebApplicationContext webApplicationContext = new AnnotationConfigWebApplicationContext();
    webApplicationContext.setParent(rootContext);
    webApplicationContext.register(SpringWebConfiguration.class);
    return webApplicationContext;
}

private static WebApplicationContext createWebContext(ApplicationContext rootContext, ServletContextHandler servletContext) throws ServletException {
    WebApplicationContext webApplicationContext = createWebContext(rootContext);

    /* enable logging of each http request */
    servletContext.addFilter(LoggingFilterChain.class, "/*", EnumSet.of(DispatcherType.REQUEST));
    registerDispatcherServlet("api", servletContext, webApplicationContext);
    return webApplicationContext;
}

private static DispatcherServlet registerDispatcherServlet(String path, ServletContextHandler servletContext, WebApplicationContext ctx) {
    DispatcherServlet servlet = new DispatcherServlet(ctx);
    servletContext.addServlet(new ServletHolder(servlet), "/" + path + "/*");
    return servlet;
}

Any idea how I have to change the configuration in order to use the mockmvc tests? I prefer a solution without spring.xml / web.xml.

Jan
  • 2,803
  • 6
  • 36
  • 57

1 Answers1

0

Sadly no answer, but after a while I got the intention of the spring developers and how a servlet context works:

In the question I used jetty to start a certain DispatcherServlet with a certain context. No magic, very clear what happens. No way to mock something in between. In order to separate the servlet container (jetty) from the actual web application context I had to:

  1. Use springs WebApplicationInitializer interface and configure the context there

  2. Let jetty find the context. That part was a little tricky because my jetty version only scans in the jar files so I decided it would be better to strongly couple my jetty with my web application:

--

WebAppContext webAppContext = new WebAppContext();
webAppContext.setConfigurations(new Configuration[]{new ConcreteConfiguration(ServletInitializer.class)}); //that class implements the WebApplicationInitializer of spring.

//...

/**
 * Configures a concrete WebApplicationInitializer
 */
static class ConcreteConfiguration extends AnnotationConfiguration {

    private final Class<?> initializerClass;

    ConcreteConfiguration(Class<?> initializerClass) {
        this.initializerClass = initializerClass;
    }

    @Override
    public void preConfigure(WebAppContext context) throws Exception {
        MultiMap<String> map = new MultiMap<String>();
        map.add(WebApplicationInitializer.class.getName(), initializerClass.getName());
        context.setAttribute(CLASS_INHERITANCE_MAP, map);
        _classInheritanceHandler = new ClassInheritanceHandler(map);
    }
}

(I found the idea for the static inner class here: https://stackoverflow.com/a/15702616/320299)

Community
  • 1
  • 1
Jan
  • 2,803
  • 6
  • 36
  • 57