Support parallel test execution with @AutoConfigureMockMvc

Previously, the deferred line writing that is used, to print MockMvc
results to the console assumed that each DeferredLinesWriter would
only be used by a single thread at a time. This assumption does not
hold true when using JUnit 5's parallel test exection if the tests
running in parallel share an application context. This resulted in
a concurrent modification exception if one thread was adding lines
to the output while another was iterating over them.

This commit updates DeferredLinesWriter so that it uses thread local
storage for the deferred lines. This ensures that each List of lines
is only ever accessed by a single thread.

Closes gh-16179
This commit is contained in:
Andy Wilkinson
2019-08-23 14:09:17 +01:00
parent 52bcdac7b0
commit 2d2e3b3d8b
2 changed files with 59 additions and 4 deletions

View File

@@ -226,7 +226,7 @@ public class SpringBootMockMvcBuilderCustomizer implements MockMvcBuilderCustomi
private final LinesWriter delegate;
private final List<String> lines = new ArrayList<>();
private final ThreadLocal<List<String>> lines = ThreadLocal.withInitial(ArrayList::new);
DeferredLinesWriter(WebApplicationContext context, LinesWriter delegate) {
Assert.state(context instanceof ConfigurableApplicationContext,
@@ -237,11 +237,11 @@ public class SpringBootMockMvcBuilderCustomizer implements MockMvcBuilderCustomi
@Override
public void write(List<String> lines) {
this.lines.addAll(lines);
this.lines.get().addAll(lines);
}
void writeDeferredResult() {
this.delegate.write(this.lines);
this.delegate.write(this.lines.get());
}
static DeferredLinesWriter get(ApplicationContext applicationContext) {
@@ -254,7 +254,7 @@ public class SpringBootMockMvcBuilderCustomizer implements MockMvcBuilderCustomi
}
void clear() {
this.lines.clear();
this.lines.get().clear();
}
}