3

I am trying to test spring rest documentation for rest API for our services using spring cucumber jvm but end up with a null pointer exeception when I try to execute the scenario, as the framework is not able to intialize the Junit context.

Error Message:

java.lang.NullPointerException at 
org.springframework.restdocs.ManualRestDocumentation.beforeO‌​peration(ManualRestD‌​ocumentation.java:90‌​) at 
org.springframework.restdocs.JUnitRestDocumentation.beforeOp‌​eration(JUnitRestDoc‌​umentation.java:76)

Code:

private AppProperties props;
@Before("@rest") public void beforeScenario() { 
     JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation( "target/generated-snippets" );
     System.out.println( "jUnitRestDocumentation " +restDocumentation );
     spec = new RequestSpecBuilder().addFilter( documentationConfiguration( restDocumentation ) ).build();
     System.out.println( "\n spec init .. " +restDocumentation );
}

Step definition code:

@Given("^create a rest document for VHR API$")
public void create_a_rest_document_for_VHR_API() throws Throwable {
    estAssured.given( spec )
        .accept( "application/json" )
        .filter( document( "vhrdocument" ) ) .when() 
        .get( props.getVhrrequesturl() + "/vhrData/{vehicleID}", "5VW4T7AU0FM029999" ) .then().log().all();
}
Joshua Drake
  • 2,704
  • 3
  • 35
  • 54
ravi
  • 81
  • 6
  • Error Message: java.lang.NullPointerException at org.springframework.restdocs.ManualRestDocumentation.beforeOperation(ManualRestDocumentation.java:90) at org.springframework.restdocs.JUnitRestDocumentation.beforeOperation(JUnitRestDocumentation.java:76) – ravi Nov 21 '17 at 21:22
  • @Autowired private AppProperties props; @Before("@rest") public void beforeScenario() { JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation( "target/generated-snippets" ); System.out.println( "jUnitRestDocumentation " +restDocumentation ); spec = new RequestSpecBuilder().addFilter( documentationConfiguration( restDocumentation ) ).build(); System.out.println( "\n spec init .. " +restDocumentation ); } – ravi Nov 21 '17 at 21:23
  • Step definition code:@Given("^create a rest document for VHR API$") public void create_a_rest_document_for_VHR_API() throws Throwable { RestAssured.given( spec ) .accept( "application/json" ) .filter( document( "vhrdocument" ) ) .when() .get( props.getVhrrequesturl() + "/vhrData/{vehicleID}", "5VW4T7AU0FM029999" ) .then().log().all(); } – ravi Nov 21 '17 at 21:23
  • 2
    Please add the comments to your question and format them properly :) – hatef Nov 21 '17 at 21:47
  • It would also be useful to know what version of REST Docs you're using – Andy Wilkinson Nov 21 '17 at 22:14
  • Included code from comments and an attempt at formatting. – Joshua Drake Nov 21 '17 at 22:29

5 Answers5

4

You aren't using JUnitRestDocumentation as it's intended to be used. It's designed to be used as a JUnit rule which means it should be a public field annotated with @Rule:

@Rule
public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();

Being a rule means that JUnit will automatically call restDocumentation for each test, allowing Spring REST Docs to set up and tear down the test-specific context. The NullPointerException is occurring because restDocumentation hasn't been called in this way and, therefore, the context hasn't been set up.

You haven't described how you're using Cucumber, but if you're using it's JUnit runner you should be able to fix the problem by declaring restDocumentation as a @Rule-annotated field as shown above. If you're not using its JUnit runner, you may need to use Spring REST Docs' ManualRestDocumentation instead. The Spring REST Docs reference documentation contains a section that describes how to set up your tests when you're not using JUnit.

Andy Wilkinson
  • 108,729
  • 24
  • 257
  • 242
4

I had the same problem because I had multiple test class inheriting the class, in which I declared the JUnitRestDocumentation instance. My mistake was that I declared the rule using the @Rule annotation. I should have used @ClassRule and declared the instance as static.

@ClassRule
public static JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();
Muhammad Gelbana
  • 3,890
  • 3
  • 43
  • 81
2

it happened with test SpockFramework, and i added to pom.xml:

<dependency>
    <groupId>org.spockframework</groupId>
    <artifactId>spock-junit4</artifactId>
    <scope>test</scope>
</dependency>
Ivan
  • 2,316
  • 2
  • 24
  • 22
  • 1
    This is the best answer. @Rule without junit4 runner would not work. Current Spock uses JUnit 5 engine and Spock does not support JUnit 5 extensions. – Cyva Mar 06 '21 at 06:16
0

I had the same symptoms when migrating from RestAssured 2.x to RestAssured 3.1.1.

The codebase had a way to setup RestAssured in order to avoid repetitive ceremony for every tests :

@Rule

public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation();

@Before
public void configure_rest_assured() {
    RestAssured.port = springServerPort;
    RestAssured.config = config().objectMapperConfig(
        objectMapperConfig().jackson2ObjectMapperFactory((cls, charset) -> customObjectMapper)
    )
    ...;
    RestAssured.requestSpecification = new RequestSpecBuilder()
        .addRequestSpecification(documentationConfiguration(docRule, ...))
        ...
        .build();
}

This was working well, until I migrated to 3.x. The issue was that new RequestSpecBuilder() will append itself to the default static RestAssured.requestSpecification.

The first test passed, but when it finished the rule was disposed (the after part), when the second test started to ran, the Before method was chaining

  1. the specification created for the first test (referencing the disposed rule used by the first test method)
  2. the specification created for the second test (referencing the active rule for second test method)

And so on as new tests are ran. But when the second test is run RestAssured invoke specification in order, e.g. the number 1, but since it was referencing a disposed rule (the beforeOperation was executed on a null context)

To fix that the code had to clear the previous specifications :

@Before
public void configure_rest_assured() {
    RestAssured.port = springServerPort;
    RestAssured.config = config().objectMapperConfig(
        objectMapperConfig().jackson2ObjectMapperFactory((cls, charset) -> customObjectMapper)
    )
    ...;
    RestAssured.requestSpecification = null; // avoid the builder to acquire previous specs.
    RestAssured.requestSpecification = new RequestSpecBuilder()
        .addRequestSpecification(documentationConfiguration(docRule, ...))
        ...
        .build();
}
bric3
  • 40,072
  • 9
  • 91
  • 111
0

For using cucumber-java-8 with spring rest docs and spring-security the following worked for me.

This is combining @AndyWilkison's answer from above but using the cucumber hooks instead of junit rules.

public class StepDefs implements En {

    @Autowired
    private WebApplicationContext context;

    private MockMvc mockMvc;
    private ManualRestDocumentation restDocumentation = new ManualRestDocumentation();

    public StepDefs() {

        BeforeStep((Scenario scenario) -> {
            restDocumentation.beforeTest(AuthenticationStepDefs.class, scenario.getName());
            mockMvc = MockMvcBuilders.webAppContextSetup(context).apply(springSecurity()).apply(documentationConfiguration(restDocumentation)).build();
        });
        AfterStep((Scenario scenario) -> {
            restDocumentation.afterTest();
        });
        When("create a rest document for VHR API", () -> {
            MvcResult result = mockMvc.perform(/*
              your normal call here
            */).
            .andDo(document("documentation")).
            .andReturn();
        }
    }
}
Wes
  • 6,697
  • 6
  • 34
  • 59