3

We think the Spring Rest Doc is great for documenting rest api. But it is based on Spring MVC Test,and we can't figure out how to use Spring MVC Test in my grails apps(Grails 3.0.5).

I tried to use a config class (with @Configuration and@ComponentScan) to scan grails components into the test context, but it seems like nothing had been loaded (when performing a http request to the mockmvc, it got 404).

I also tried to configure the grails controller directly, and got a run time error.

Could not autowire field: private reactor.bus.EventBus

I also tried to add @Integration (from grails) on the test class but recieved the same error.

Please help.

Here are some code samples. what i tried was adding config class or class locations or grails controller to ContextConfiguration of the test class below. And the test class itself is basically following spring rest doc reference.

import org.junit.Before;
import org.junit.Rule;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.restdocs.RestDocumentation;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration
//TODO how to scan Grails components into the test context
public class QuestionRestSpec {
@Rule
public final RestDocumentation restDocumentation = new RestDocumentation("build/generated-snippets");
@Autowired
private WebApplicationContext context;

private MockMvc mockMvc;

@Before
public void setUp() {
    this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
            .apply(documentationConfiguration(this.restDocumentation))
            .build();
}
}

The config class(which has no use):

@Configuration
@EnableWebMvc
@ComponentScan
public class AskConfig {
}
bitmountain
  • 95
  • 2
  • 9
  • Which version of Grails are you using? – Andy Wilkinson Oct 22 '15 at 08:30
  • i am using Grails 3.0.5 – bitmountain Oct 22 '15 at 09:47
  • Grails 3 is just a Spring Boot app, so, in theory at least, it should be possible although perhaps not really the Grails way. `@Integration` and injecting the `ApplicationContext` using `@Autowired` would be what I'd try. Can you share your attempt at that which didn't work? – Andy Wilkinson Oct 22 '15 at 12:42
  • I am not quite sure what do u mean by **@Integration and injecting the ApplicationContext using @Autowired.** i updated the question by adding some code samples. Basically, it is following the `spring rest doc` reference. – bitmountain Oct 23 '15 at 01:01

1 Answers1

2

Unfortunately it doesn't appear to be possible to use MockMvc with Grails. Calls to MockMvc.perform fail:

HandlerMapping requires a Grails web request. Stacktrace follows:
java.lang.IllegalArgumentException: HandlerMapping requires a Grails web request
    at org.springframework.util.Assert.notNull(Assert.java:112) ~[spring-core-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.grails.web.mapping.mvc.UrlMappingsHandlerMapping.getHandlerInternal(UrlMappingsHandlerMapping.groovy:113) ~[grails-web-url-mappings-3.0.9.jar:3.0.9]
    at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:299) ~[spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1120) [spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.test.web.servlet.TestDispatcherServlet.getHandler(TestDispatcherServlet.java:90) [spring-test-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:932) [spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:893) [spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:967) [spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:858) [spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:622) [tomcat-embed-core-8.0.26.jar:8.0.26]
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:843) [spring-webmvc-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:65) [spring-test-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729) [tomcat-embed-core-8.0.26.jar:8.0.26]
    at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:167) [spring-test-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:134) [spring-test-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:144) [spring-test-4.1.7.RELEASE.jar:4.1.7.RELEASE]
    …

Spring REST Docs 1.1 will hopefully add support for REST Assured. This removes the need for MockMvc and, instead, will allow the API to be documented by making HTTP calls to a running server in a functional test.

For reference, here's the Spock specification that I used:

package com.example.notes

import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import grails.test.mixin.integration.Integration
import grails.transaction.*
import org.junit.Rule;
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.restdocs.RestDocumentation;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext

import spock.lang.*

@Integration
@Rollback
class ApiDocumentationSpec extends Specification {

    @Rule
    public final RestDocumentation restDocumentation = new RestDocumentation("build/generated-snippets");

    @Autowired
    WebApplicationContext context

    MockMvc mockMvc

    def setup() {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
                .apply(documentationConfiguration(this.restDocumentation)).build();
    }

    def cleanup() {

    }

    void "list notes"() {
        when:
            MvcResult result = this.mockMvc.perform(get("/notes")).andReturn()
        then:
            result.andExpect(status().isOk())
                .andDo(document("notes-list-example"));
    }
}
Andy Wilkinson
  • 108,729
  • 24
  • 257
  • 242