spring-bootgroovyspockasciidocspring-restdocs

Generate ascii doc api documentation from spring rest docs using Spock


I'm trying to write Spock test cases for my Spring Boot application (version: 2.7.5), and I want to generate AsciiDoc documentation using Spring REST Docs. Can someone provide a sample working code snippet demonstrating how to achieve this?

Here's what I've tried so far:

build.gradle

plugins { 
    id 'org.springframework.boot' version '2.7.5'
    id 'io.spring.dependency-management' version '1.0.15.RELEASE'
    id 'groovy'
    id "org.asciidoctor.convert" version "1.5.8.1"
}

group = 'co.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
    mavenCentral()
}

ext { 
    snippetsDir = file('build/generated-snippets')
}

dependencies {
     ...
     implementation 'org.springframework.restdocs:spring-restdocs-restassured:2.0.5.RELEASE'
    implementation 'org.springframework.restdocs:spring-restdocs-core:2.0.5.RELEASE'
    implementation 'org.springframework.restdocs:spring-restdocs-asciidoctor:2.0.5.RELEASE'
    testImplementation 'junit:junit:4.13.2'
    ...
}


test { 
    outputs.dir snippetsDir
}

asciidoctor { 
    inputs.dir snippetsDir 
    dependsOn test 
}

LeadControllerSpec

class LeadControllerSpec extends Specification {

    @Rule
    JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation() // TODO: need to give path

   
    RequestSpecification documentationSpec

    void setup() {
        this.documentationSpec = new RequestSpecBuilder()
                .addFilter(RestAssuredRestDocumentation.documentationConfiguration(restDocumentation)
                .operationPreprocessors()
                        .withRequestDefaults(
                                Preprocessors.modifyUris().host('api.example.com')
                                        .removePort())
                        .withResponseDefaults(Preprocessors.prettyPrint()))
                .build()
    }

    void cleanup() {

    }

    def "test createLead endpoint"() {
        when:
        def response = RestAssured.given(this.documentationSpec)
                .accept(MediaType.APPLICATION_JSON_VALUE)
                .contentType(MediaType.APPLICATION_JSON_VALUE)
                .body([
                        firstName: "John",
                        lastName: "Doe",
                        mobileNumber: "1234567890",
                        emailId: "john@example.com",
                        pincode: "12345",
                        cardScheme: "SchemeA"
                ])
                .when()
                .post("/api/v1/lead/create")

        then:
        response.statusCode == 200
    }
}

Any help would be appreciated. Thanks in advance!


Solution

  • Your code with @Rule looks like JUnit 4, but Spock 2.x is based on JUnit 5, i.e. that approach probably does not work, unless maybe you are working with Spock 1.x.

    I was curious and tried to set up a Maven project from scratch - sorry, please convert it to Gradle by yourself:

    https://github.com/kriegaex/SO_Spock_SpringRESTDocs_78424426

    The sample application is as follows:

    package org.example;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @SpringBootApplication
    public class Application {
      public static void main(String[] args) {
        new SpringApplication(Application.class).run(args);
      }
    
      @RestController
      private static class SampleController {
        @RequestMapping("/")
        public String index() {
          return "Hello, World";
        }
      }
    }
    

    The corresponding Spock spec looks like this:

    package org.example
    
    import org.springframework.beans.factory.annotation.Autowired
    import org.springframework.boot.test.context.SpringBootTest
    import org.springframework.restdocs.ManualRestDocumentation
    import org.springframework.test.web.servlet.MockMvc
    import org.springframework.test.web.servlet.setup.MockMvcBuilders
    import org.springframework.web.context.WebApplicationContext
    import spock.lang.Specification
    
    import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document
    import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
    
    @SpringBootTest(classes = [Application])
    class MyControllerSpec extends Specification {
      def restDocumentation = new ManualRestDocumentation()
      @Autowired
      WebApplicationContext context
      MockMvc mockMvc
    
      def setup() {
        mockMvc = MockMvcBuilders
          .webAppContextSetup(context)
          .apply(documentationConfiguration(restDocumentation))
          .build()
        restDocumentation.beforeTest(getClass(), specificationContext.currentFeature.displayName)
      }
    
      def cleanup() {
        restDocumentation.afterTest()
      }
    
      def "should document the GET /hello endpoint"() {
        expect:
        mockMvc
          .perform(get("/"))
          .andExpect(status().isOk())
          .andDo(document("sample"))
      }
    }
    

    Basically, I adjusted the manual setup described in the manual for non-JUnit setups to Spock. From the provided sample project I copied code for TestNG.

    It was a bit tricky to make the various product versions for Spring and Spring REST Docs match and also play nice with Spock, but in my sample project it works fine now. The generated HTML page from the asciidoc with included snippets looks as follows:

    Generated HTML docs from asciidoc

    Disclaimer: Chances are, that my configuration is imperfect and can be improved. But I never used Spring REST Docs before, and I do not even know Spring.