From 02c4a221436bc6feb467d284fd607ab614ac8594 Mon Sep 17 00:00:00 2001 From: Daniel Lin Date: Fri, 10 Jul 2020 17:50:57 -0400 Subject: [PATCH 1/2] Add JUnit 5 Expect extension --- extensions/junit5/pom.xml | 45 ++++++++++ .../google/common/truth/junit5/Expect.java | 82 +++++++++++++++++++ .../org.junit.jupiter.api.extension.Extension | 1 + extensions/pom.xml | 1 + pom.xml | 10 +++ 5 files changed, 139 insertions(+) create mode 100644 extensions/junit5/pom.xml create mode 100644 extensions/junit5/src/main/java/com/google/common/truth/junit5/Expect.java create mode 100644 extensions/junit5/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension diff --git a/extensions/junit5/pom.xml b/extensions/junit5/pom.xml new file mode 100644 index 000000000..517fab6c5 --- /dev/null +++ b/extensions/junit5/pom.xml @@ -0,0 +1,45 @@ + + + 4.0.0 + + com.google.truth.extensions + truth-extensions-parent + HEAD-SNAPSHOT + + truth-junit5-extension + Truth Extension for JUnit Jupiter + + An extension for JUnit Jupiter to use soft assertions with Truth test assertion framework + + + + com.google.truth + truth + + + org.junit.jupiter + junit-jupiter-api + + + org.opentest4j + opentest4j + + + + + + maven-javadoc-plugin + + + maven-compiler-plugin + + 1.8 + 1.8 + + + + + diff --git a/extensions/junit5/src/main/java/com/google/common/truth/junit5/Expect.java b/extensions/junit5/src/main/java/com/google/common/truth/junit5/Expect.java new file mode 100644 index 000000000..3e3f14f89 --- /dev/null +++ b/extensions/junit5/src/main/java/com/google/common/truth/junit5/Expect.java @@ -0,0 +1,82 @@ +package com.google.common.truth.junit5; + +import com.google.common.truth.FailureStrategy; +import com.google.common.truth.StandardSubjectBuilder; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.Extension; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.opentest4j.MultipleFailuresError; + +import java.util.ArrayList; +import java.util.List; + +/** + * An Extension that provides a {@link StandardSubjectBuilder} parameter to test methods. + * + *

Assertion failures on the given StandardSubjectBuilder will not immediately fail; + * instead, failures will be reported at the end of the test. + * + *

Usage: + * + *

+ * @ExtendWith(Expect.class) // or system property junit.jupiter.extensions.autodetection.enabled=true
+ * ...
+ *     @Test
+ *     public void test(StandardSubjectBuilder expect) {
+ *         ...
+ *     }
+ * 
+ */ +public class Expect implements Extension, ParameterResolver, AfterEachCallback { + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return parameterContext.getParameter().getType() == StandardSubjectBuilder.class; + } + + @Override + public StandardSubjectBuilder resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + ExtensionContext.Store store = extensionContext.getStore(ExtensionContext.Namespace.create(Expect.class)); + return store.getOrComputeIfAbsent(extensionContext.getUniqueId(), key -> new Holder(), Holder.class).get(); + } + + @Override + public void afterEach(ExtensionContext context) { + ExtensionContext.Store store = context.getStore(ExtensionContext.Namespace.create(Expect.class)); + Holder holder = store.get(context.getUniqueId(), Holder.class); + List failures = null; + if (holder != null) { + synchronized (holder) { + failures = holder.failures; + if (failures == null) { + throw new IllegalStateException(); + } + holder.failures = null; + } + } + if (failures != null && !failures.isEmpty()) { + throw failures.size() == 1 ? failures.get(0) : new MultipleFailuresError(null, failures); + } + } + + private static class Holder implements FailureStrategy, ExtensionContext.Store.CloseableResource { + private List failures = new ArrayList<>(); + + private StandardSubjectBuilder get() { + return StandardSubjectBuilder.forCustomFailureStrategy(this); + } + + @Override + public synchronized void fail(AssertionError failure) { + failures.add(failure); + } + + @Override + public synchronized void close() { + if (failures != null) { + throw new IllegalStateException(); + } + } + } +} diff --git a/extensions/junit5/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension b/extensions/junit5/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension new file mode 100644 index 000000000..a8027f572 --- /dev/null +++ b/extensions/junit5/src/main/resources/META-INF/services/org.junit.jupiter.api.extension.Extension @@ -0,0 +1 @@ +com.google.common.truth.junit5.Expect \ No newline at end of file diff --git a/extensions/pom.xml b/extensions/pom.xml index a3af92922..9addb6c01 100644 --- a/extensions/pom.xml +++ b/extensions/pom.xml @@ -22,5 +22,6 @@ re2j liteproto proto + junit5 diff --git a/pom.xml b/pom.xml index f05917483..f7b2bcf54 100644 --- a/pom.xml +++ b/pom.xml @@ -79,6 +79,16 @@ junit 4.12 + + org.junit.jupiter + junit-jupiter-api + 5.6.2 + + + org.opentest4j + opentest4j + 1.2.0 + com.google.gwt gwt-user From 003ae1050c218a1c57d18359d14ea2f1f4169d2d Mon Sep 17 00:00:00 2001 From: Daniel Lin Date: Sat, 11 Jul 2020 15:27:51 -0400 Subject: [PATCH 2/2] Add tests to JUnit 5 extension --- extensions/junit5/pom.xml | 10 +++ .../google/common/truth/junit5/Expect.java | 80 +++++++++---------- .../common/truth/junit5/ExpectTest.java | 59 ++++++++++++++ pom.xml | 12 ++- 4 files changed, 120 insertions(+), 41 deletions(-) create mode 100644 extensions/junit5/src/test/java/com/google/common/truth/junit5/ExpectTest.java diff --git a/extensions/junit5/pom.xml b/extensions/junit5/pom.xml index 517fab6c5..16739d711 100644 --- a/extensions/junit5/pom.xml +++ b/extensions/junit5/pom.xml @@ -23,6 +23,16 @@ org.junit.jupiter junit-jupiter-api + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.platform + junit-platform-testkit + test + org.opentest4j opentest4j diff --git a/extensions/junit5/src/main/java/com/google/common/truth/junit5/Expect.java b/extensions/junit5/src/main/java/com/google/common/truth/junit5/Expect.java index 3e3f14f89..216812c99 100644 --- a/extensions/junit5/src/main/java/com/google/common/truth/junit5/Expect.java +++ b/extensions/junit5/src/main/java/com/google/common/truth/junit5/Expect.java @@ -30,53 +30,53 @@ * */ public class Expect implements Extension, ParameterResolver, AfterEachCallback { - @Override - public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { - return parameterContext.getParameter().getType() == StandardSubjectBuilder.class; - } + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + return parameterContext.getParameter().getType() == StandardSubjectBuilder.class; + } - @Override - public StandardSubjectBuilder resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { - ExtensionContext.Store store = extensionContext.getStore(ExtensionContext.Namespace.create(Expect.class)); - return store.getOrComputeIfAbsent(extensionContext.getUniqueId(), key -> new Holder(), Holder.class).get(); - } + @Override + public StandardSubjectBuilder resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) { + ExtensionContext.Store store = extensionContext.getStore(ExtensionContext.Namespace.create(Expect.class)); + return store.getOrComputeIfAbsent(extensionContext.getUniqueId(), key -> new Holder(), Holder.class).get(); + } - @Override - public void afterEach(ExtensionContext context) { - ExtensionContext.Store store = context.getStore(ExtensionContext.Namespace.create(Expect.class)); - Holder holder = store.get(context.getUniqueId(), Holder.class); - List failures = null; - if (holder != null) { - synchronized (holder) { - failures = holder.failures; - if (failures == null) { - throw new IllegalStateException(); - } - holder.failures = null; - } - } - if (failures != null && !failures.isEmpty()) { - throw failures.size() == 1 ? failures.get(0) : new MultipleFailuresError(null, failures); + @Override + public void afterEach(ExtensionContext context) { + ExtensionContext.Store store = context.getStore(ExtensionContext.Namespace.create(Expect.class)); + Holder holder = store.get(context.getUniqueId(), Holder.class); + List failures = null; + if (holder != null) { + synchronized (holder) { + failures = holder.failures; + if (failures == null) { + throw new IllegalStateException(); } + holder.failures = null; + } + } + if (failures != null && !failures.isEmpty()) { + throw failures.size() == 1 ? failures.get(0) : new MultipleFailuresError(null, failures); } + } - private static class Holder implements FailureStrategy, ExtensionContext.Store.CloseableResource { - private List failures = new ArrayList<>(); + private static class Holder implements FailureStrategy, ExtensionContext.Store.CloseableResource { + private List failures = new ArrayList<>(); - private StandardSubjectBuilder get() { - return StandardSubjectBuilder.forCustomFailureStrategy(this); - } + private StandardSubjectBuilder get() { + return StandardSubjectBuilder.forCustomFailureStrategy(this); + } - @Override - public synchronized void fail(AssertionError failure) { - failures.add(failure); - } + @Override + public synchronized void fail(AssertionError failure) { + failures.add(failure); + } - @Override - public synchronized void close() { - if (failures != null) { - throw new IllegalStateException(); - } - } + @Override + public synchronized void close() { + if (failures != null) { + throw new IllegalStateException(); + } } + } } diff --git a/extensions/junit5/src/test/java/com/google/common/truth/junit5/ExpectTest.java b/extensions/junit5/src/test/java/com/google/common/truth/junit5/ExpectTest.java new file mode 100644 index 000000000..e3d106637 --- /dev/null +++ b/extensions/junit5/src/test/java/com/google/common/truth/junit5/ExpectTest.java @@ -0,0 +1,59 @@ +package com.google.common.truth.junit5; + +import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass; +import static org.junit.platform.testkit.engine.EventConditions.event; +import static org.junit.platform.testkit.engine.EventConditions.finishedWithFailure; +import static org.junit.platform.testkit.engine.EventConditions.test; + +import com.google.common.truth.StandardSubjectBuilder; +import org.assertj.core.api.Condition; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.platform.testkit.engine.EngineTestKit; +import org.junit.platform.testkit.engine.Events; +import org.opentest4j.MultipleFailuresError; + +public class ExpectTest { + @Test + public void testExtension() { + Events events = EngineTestKit.engine("junit-jupiter") + .selectors(selectClass(TestCase.class)) + .execute() + .testEvents() + .assertStatistics(stats -> stats.started(3).failed(2)); + events.succeeded().assertEventsMatchExactly(event(test("testSuccess"))); + events.failed().assertEventsMatchExactly( + event(test("testOneFailure"), finishedWithFailure(new Condition(t -> t instanceof AssertionError && !(t instanceof MultipleFailuresError), "AssertionError"))), + event(test("testTwoFailures"), finishedWithFailure(new Condition(MultipleFailuresError.class::isInstance, "MultipleFailuresError")))); + } + + @ExtendWith(Expect.class) + @TestMethodOrder(OrderAnnotation.class) + static class TestCase { + @Test + @Order(1) + public void testSuccess(StandardSubjectBuilder expect) { + expect.that(0).isEqualTo(0); + expect.that(1).isEqualTo(1); + } + + @Test + @Order(2) + @ExtendWith(Expect.class) + public void testOneFailure(StandardSubjectBuilder expect) { + expect.that(0).isEqualTo(1); + expect.that(1).isEqualTo(1); + } + + @Test + @Order(3) + @ExtendWith(Expect.class) + public void testTwoFailures(StandardSubjectBuilder expect) { + expect.that(0).isEqualTo(1); + expect.that(1).isEqualTo(0); + } + } +} diff --git a/pom.xml b/pom.xml index f7b2bcf54..2c39ca17f 100644 --- a/pom.xml +++ b/pom.xml @@ -84,6 +84,16 @@ junit-jupiter-api 5.6.2 + + org.junit.jupiter + junit-jupiter-engine + 5.6.2 + + + org.junit.platform + junit-platform-testkit + 1.6.2 + org.opentest4j opentest4j @@ -332,7 +342,7 @@ maven-surefire-plugin - 2.12.4 + 2.22.2 maven-enforcer-plugin