Commit bc14dce1 authored by Andy Wilkinson's avatar Andy Wilkinson

Merge pull request #17049 from sbrannen

* gh-17049:
  Polish "Polish OutputCapture and its JUnit Jupiter extension"
  Polish OutputCapture and its JUnit Jupiter extension

Closes gh-17049
parents b092cb7b 41957ec2
...@@ -23,7 +23,7 @@ package org.springframework.boot.test.system; ...@@ -23,7 +23,7 @@ package org.springframework.boot.test.system;
* standard JUnit assertions. For example: <pre class="code"> * standard JUnit assertions. For example: <pre class="code">
* assertThat(output).contains("started"); // Checks all output * assertThat(output).contains("started"); // Checks all output
* assertThat(output.getErr()).contains("failed"); // Only checks System.err * assertThat(output.getErr()).contains("failed"); // Only checks System.err
* assertThat(output.getOut()).contains("ok"); // Only checks System.put * assertThat(output.getOut()).contains("ok"); // Only checks System.out
* </pre> * </pre>
* *
* @author Madhura Bhave * @author Madhura Bhave
......
...@@ -38,6 +38,7 @@ import org.springframework.util.ClassUtils; ...@@ -38,6 +38,7 @@ import org.springframework.util.ClassUtils;
* @author Madhura Bhave * @author Madhura Bhave
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Sam Brannen
* @see OutputCaptureExtension * @see OutputCaptureExtension
* @see OutputCaptureRule * @see OutputCaptureRule
*/ */
...@@ -125,7 +126,8 @@ class OutputCapture implements CapturedOutput { ...@@ -125,7 +126,8 @@ class OutputCapture implements CapturedOutput {
} }
private String get(Predicate<Type> filter) { private String get(Predicate<Type> filter) {
Assert.state(!this.systemCaptures.isEmpty(), "No system captures found. Check that you have used @ExtendWith."); Assert.state(!this.systemCaptures.isEmpty(),
"No system captures found. Please check your output capture registration.");
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
for (SystemCapture systemCapture : this.systemCaptures) { for (SystemCapture systemCapture : this.systemCaptures) {
systemCapture.append(builder, filter); systemCapture.append(builder, filter);
......
...@@ -22,19 +22,21 @@ import org.junit.jupiter.api.extension.BeforeAllCallback; ...@@ -22,19 +22,21 @@ import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
import org.junit.jupiter.api.extension.ExtensionContext.Store;
import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException; import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver; import org.junit.jupiter.api.extension.ParameterResolver;
/** /**
* JUnit 5 {@code @Extension} to capture {@link System#out System.out} and * JUnit Jupiter {@code @Extension} to capture {@link System#out System.out} and
* {@link System#err System.err}. Can be used on a test class via * {@link System#err System.err}. Can be registered for an entire test class or for an
* {@link ExtendWith @ExtendWith}. This extension provides {@link ParameterResolver * individual test method via {@link ExtendWith @ExtendWith}. This extension provides
* parameter resolution} for a {@link CapturedOutput} instance which can be used to assert * {@linkplain ParameterResolver parameter resolution} for a {@link CapturedOutput}
* that the correct output was written. * instance which can be used to assert that the correct output was written.
* <p> * <p>
* To use, add {@link ExtendWith @ExtendWith} and inject the {@link CapturedOutput} as an * To use with {@link ExtendWith @ExtendWith}, inject the {@link CapturedOutput} as an
* argument to your test class constructor or test method: * argument to your test class constructor, test method, or lifecycle methods:
* *
* <pre class="code"> * <pre class="code">
* &#064;ExtendWith(OutputCaptureExtension.class) * &#064;ExtendWith(OutputCaptureExtension.class)
...@@ -42,7 +44,15 @@ import org.junit.jupiter.api.extension.ParameterResolver; ...@@ -42,7 +44,15 @@ import org.junit.jupiter.api.extension.ParameterResolver;
* *
* &#064;Test * &#064;Test
* void test(CapturedOutput output) { * void test(CapturedOutput output) {
* System.out.println("ok");
* assertThat(output).contains("ok"); * assertThat(output).contains("ok");
* System.err.println("error");
* }
*
* &#064;AfterEach
* void after(CapturedOutput output) {
* assertThat(output.getOut()).contains("ok");
* assertThat(output.getErr()).contains("error");
* } * }
* *
* } * }
...@@ -51,36 +61,35 @@ import org.junit.jupiter.api.extension.ParameterResolver; ...@@ -51,36 +61,35 @@ import org.junit.jupiter.api.extension.ParameterResolver;
* @author Madhura Bhave * @author Madhura Bhave
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Sam Brannen
* @since 2.2.0 * @since 2.2.0
* @see CapturedOutput * @see CapturedOutput
*/ */
public class OutputCaptureExtension public class OutputCaptureExtension
implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, ParameterResolver { implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, ParameterResolver {
private final OutputCapture outputCapture = new OutputCapture();
OutputCaptureExtension() { OutputCaptureExtension() {
// Package private to prevent users from directly creating an instance. // Package private to prevent users from directly creating an instance.
} }
@Override @Override
public void beforeAll(ExtensionContext context) throws Exception { public void beforeAll(ExtensionContext context) throws Exception {
this.outputCapture.push(); getOutputCapture(context).push();
} }
@Override @Override
public void afterAll(ExtensionContext context) throws Exception { public void afterAll(ExtensionContext context) throws Exception {
this.outputCapture.pop(); getOutputCapture(context).pop();
} }
@Override @Override
public void beforeEach(ExtensionContext context) throws Exception { public void beforeEach(ExtensionContext context) throws Exception {
this.outputCapture.push(); getOutputCapture(context).push();
} }
@Override @Override
public void afterEach(ExtensionContext context) throws Exception { public void afterEach(ExtensionContext context) throws Exception {
this.outputCapture.pop(); getOutputCapture(context).pop();
} }
@Override @Override
...@@ -90,9 +99,16 @@ public class OutputCaptureExtension ...@@ -90,9 +99,16 @@ public class OutputCaptureExtension
} }
@Override @Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
throws ParameterResolutionException { return getOutputCapture(extensionContext);
return this.outputCapture; }
private OutputCapture getOutputCapture(ExtensionContext context) {
return getStore(context).getOrComputeIfAbsent(OutputCapture.class);
}
private Store getStore(ExtensionContext context) {
return context.getStore(Namespace.create(getClass()));
} }
} }
...@@ -111,7 +111,8 @@ class OutputCapture implements CapturedOutput { ...@@ -111,7 +111,8 @@ class OutputCapture implements CapturedOutput {
} }
private String get(Predicate<Type> filter) { private String get(Predicate<Type> filter) {
Assert.state(!this.systemCaptures.isEmpty(), "No system captures found. Check that you have used @ExtendWith."); Assert.state(!this.systemCaptures.isEmpty(),
"No system captures found. Please check your output capture registration.");
StringBuilder builder = new StringBuilder(); StringBuilder builder = new StringBuilder();
for (SystemCapture systemCapture : this.systemCaptures) { for (SystemCapture systemCapture : this.systemCaptures) {
systemCapture.append(builder, filter); systemCapture.append(builder, filter);
......
...@@ -20,47 +20,76 @@ import org.junit.jupiter.api.extension.AfterAllCallback; ...@@ -20,47 +20,76 @@ import org.junit.jupiter.api.extension.AfterAllCallback;
import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
import org.junit.jupiter.api.extension.ExtensionContext.Store;
import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException; import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver; import org.junit.jupiter.api.extension.ParameterResolver;
/** /**
* Internal JUnit 5 {@code @Extension} to capture {@link System#out System.out} and * JUnit Jupiter {@code @Extension} to capture {@link System#out System.out} and
* {@link System#err System.err}. * {@link System#err System.err}. Can be registered for an entire test class or for an
* individual test method via {@link ExtendWith @ExtendWith}. This extension provides
* {@linkplain ParameterResolver parameter resolution} for a {@link CapturedOutput}
* instance which can be used to assert that the correct output was written.
* <p>
* To use with {@link ExtendWith @ExtendWith}, inject the {@link CapturedOutput} as an
* argument to your test class constructor, test method, or lifecycle methods:
*
* <pre class="code">
* &#064;ExtendWith(OutputCaptureExtension.class)
* class MyTest {
*
* &#064;Test
* void test(CapturedOutput output) {
* System.out.println("ok");
* assertThat(output).contains("ok");
* System.err.println("error");
* }
*
* &#064;AfterEach
* void after(CapturedOutput output) {
* assertThat(output.getOut()).contains("ok");
* assertThat(output.getErr()).contains("error");
* }
*
* }
* </pre>
* *
* @author Madhura Bhave * @author Madhura Bhave
* @author Phillip Webb * @author Phillip Webb
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Sam Brannen
* @since 2.2.0 * @since 2.2.0
* @see CapturedOutput
*/ */
public class OutputCaptureExtension public class OutputCaptureExtension
implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, ParameterResolver { implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, ParameterResolver {
private final OutputCapture outputCapture = new OutputCapture();
OutputCaptureExtension() { OutputCaptureExtension() {
// Package private to prevent users from directly creating an instance. // Package private to prevent users from directly creating an instance.
} }
@Override @Override
public void beforeAll(ExtensionContext context) throws Exception { public void beforeAll(ExtensionContext context) throws Exception {
this.outputCapture.push(); getOutputCapture(context).push();
} }
@Override @Override
public void afterAll(ExtensionContext context) throws Exception { public void afterAll(ExtensionContext context) throws Exception {
this.outputCapture.pop(); getOutputCapture(context).pop();
} }
@Override @Override
public void beforeEach(ExtensionContext context) throws Exception { public void beforeEach(ExtensionContext context) throws Exception {
this.outputCapture.push(); getOutputCapture(context).push();
} }
@Override @Override
public void afterEach(ExtensionContext context) throws Exception { public void afterEach(ExtensionContext context) throws Exception {
this.outputCapture.pop(); getOutputCapture(context).pop();
} }
@Override @Override
...@@ -70,9 +99,16 @@ public class OutputCaptureExtension ...@@ -70,9 +99,16 @@ public class OutputCaptureExtension
} }
@Override @Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) {
throws ParameterResolutionException { return getOutputCapture(extensionContext);
return this.outputCapture; }
private OutputCapture getOutputCapture(ExtensionContext context) {
return getStore(context).getOrComputeIfAbsent(OutputCapture.class);
}
private Store getStore(ExtensionContext context) {
return context.getStore(Namespace.create(getClass()));
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment