Make JUnit Jupiter extension stateless so it can be used in parallel
Previously, REST Docs' JUnit Jupiter extension, RestDocumentationExtension, stored its own state. Since the code was written I have learned that this isn't recommended and that the extension context's store should be used instead. Up until now, we'd got away with the extension being stateful, but the introduction of parallel test execution has highlighted the problem. This commit updates RestDocumentationExtension to use the store from the execution context rather than storing its own state. An unwanted, but necessary, side-effect of this is that RestDocumentationExtension itself no longer implements RestDocumentationContextProvider and, instead, returns a new type rather than itself when asked to resolve the provider. This is technically a breaking API change, but users will be unaffected if they have followed the recommended and documented approach of injecting RestDocumentationContextProvider. Closes gh-520
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,6 +26,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.restdocs.RestDocumentationContextProvider;
|
||||
import org.springframework.restdocs.RestDocumentationExtension;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
@@ -42,7 +43,7 @@ public class SampleJUnit5ApplicationTests {
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp(RestDocumentationExtension restDocumentation) {
|
||||
public void setUp(RestDocumentationContextProvider restDocumentation) {
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(context)
|
||||
.apply(documentationConfiguration(restDocumentation)).build();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2014-2017 the original author or authors.
|
||||
* Copyright 2014-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,12 +16,11 @@
|
||||
|
||||
package org.springframework.restdocs;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.junit.jupiter.api.extension.AfterEachCallback;
|
||||
import org.junit.jupiter.api.extension.BeforeEachCallback;
|
||||
import org.junit.jupiter.api.extension.Extension;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
|
||||
import org.junit.jupiter.api.extension.ParameterContext;
|
||||
import org.junit.jupiter.api.extension.ParameterResolver;
|
||||
|
||||
@@ -31,28 +30,18 @@ import org.junit.jupiter.api.extension.ParameterResolver;
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class RestDocumentationExtension implements Extension, BeforeEachCallback,
|
||||
AfterEachCallback, RestDocumentationContextProvider, ParameterResolver {
|
||||
|
||||
private final ManualRestDocumentation delegate = new ManualRestDocumentation();
|
||||
public class RestDocumentationExtension
|
||||
implements BeforeEachCallback, AfterEachCallback, ParameterResolver {
|
||||
|
||||
@Override
|
||||
public void beforeEach(ExtensionContext context) throws Exception {
|
||||
Class<?> testClass = context.getTestClass().orElseThrow(
|
||||
() -> new IllegalStateException("No test class was available"));
|
||||
Method testMethod = context.getTestMethod().orElseThrow(
|
||||
() -> new IllegalStateException("No test method was available"));
|
||||
this.delegate.beforeTest(testClass, testMethod.getName());
|
||||
this.getDelegate(context).beforeTest(context.getRequiredTestClass(),
|
||||
context.getRequiredTestMethod().getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterEach(ExtensionContext context) throws Exception {
|
||||
this.delegate.afterTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestDocumentationContext beforeOperation() {
|
||||
return this.delegate.beforeOperation();
|
||||
this.getDelegate(context).afterTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,8 +53,16 @@ public class RestDocumentationExtension implements Extension, BeforeEachCallback
|
||||
|
||||
@Override
|
||||
public Object resolveParameter(ParameterContext parameterContext,
|
||||
ExtensionContext extensionContext) {
|
||||
return this;
|
||||
ExtensionContext context) {
|
||||
return (RestDocumentationContextProvider) () -> getDelegate(context)
|
||||
.beforeOperation();
|
||||
}
|
||||
|
||||
private ManualRestDocumentation getDelegate(ExtensionContext context) {
|
||||
Namespace namespace = Namespace.create(getClass(), context.getUniqueId());
|
||||
return context.getStore(namespace).getOrComputeIfAbsent(
|
||||
ManualRestDocumentation.class, (key) -> new ManualRestDocumentation(),
|
||||
ManualRestDocumentation.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user