From ed2b81229e06a9c30a7209f807118d4b90190555 Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Thu, 10 Oct 2024 16:13:59 +0200 Subject: [PATCH] Add tests for extension context scope supplied to InvocationInterceptor --- .../extension/InvocationInterceptorTests.java | 102 +++++++++++++++--- 1 file changed, 85 insertions(+), 17 deletions(-) diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/InvocationInterceptorTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/InvocationInterceptorTests.java index 2557a8173236..4f9b6391f5fb 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/InvocationInterceptorTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/engine/extension/InvocationInterceptorTests.java @@ -10,6 +10,8 @@ package org.junit.jupiter.engine.extension; +import static java.util.function.Function.identity; +import static java.util.function.Predicate.isEqual; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; @@ -22,7 +24,10 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; -import java.util.EnumSet; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.function.UnaryOperator; import java.util.stream.Stream; import org.junit.jupiter.api.AfterAll; @@ -30,9 +35,12 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DynamicTest; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestFactory; +import org.junit.jupiter.api.TestMethodOrder; import org.junit.jupiter.api.TestReporter; import org.junit.jupiter.api.extension.DynamicTestInvocationContext; import org.junit.jupiter.api.extension.ExtendWith; @@ -42,8 +50,6 @@ import org.junit.jupiter.api.extension.ReflectiveInvocationContext; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.engine.AbstractJupiterTestEngineTests; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.EnumSource; import org.junit.platform.commons.JUnitException; import org.junit.platform.engine.reporting.ReportEntry; import org.junit.platform.testkit.engine.EngineExecutionResults; @@ -130,28 +136,76 @@ void test() { } } - @ParameterizedTest(name = "{0}") - @EnumSource(InvocationType.class) - void callsInterceptors(InvocationType invocationType) { + @TestFactory + Stream callsInterceptors() { var results = executeTestsForClass(TestCaseWithThreeInterceptors.class); results.testEvents().assertStatistics(stats -> stats.failed(0).succeeded(3)); - assertThat(getEvents(results, EnumSet.of(invocationType)).distinct()) // - .containsExactly("before:foo", "before:bar", "before:baz", "test", "after:baz", "after:bar", - "after:foo"); + return Arrays.stream(InvocationType.values()) // + .map(it -> dynamicTest(it.name(), () -> verifyEvents(results, it))); } - private Stream getEvents(EngineExecutionResults results, EnumSet types) { - return results.allEvents().reportingEntryPublished() // - .map(event -> event.getPayload(ReportEntry.class).orElseThrow()) // - .map(ReportEntry::getKeyValuePairs) // - .filter(map -> map.keySet().stream().map(InvocationType::valueOf).anyMatch(types::contains)) // - .flatMap(map -> map.values().stream()); + private void verifyEvents(EngineExecutionResults results, InvocationType invocationType) { + var beforeEvents = List.of("before:foo", "before:bar", "before:baz"); + var testEvent = List.of("test"); + var afterEvents = List.of("after:baz", "after:bar", "after:foo"); + var allEvents = Stream.of(beforeEvents, testEvent, afterEvents).flatMap(Collection::stream).toList(); + String testClassName = TestCaseWithThreeInterceptors.class.getName(); + + var expectedElements = switch (invocationType) { + case BEFORE_ALL, AFTER_ALL -> prefixed(allEvents, testClassName); + case CONSTRUCTOR -> concatStreams( + prefixed(allEvents, it -> it.endsWith(":bar") ? testClassName : "test(TestReporter)"), + prefixed(allEvents, it -> it.endsWith(":bar") ? testClassName : "testTemplate(TestReporter)[1]"), + prefixed(allEvents, it -> it.endsWith(":bar") ? testClassName : "testFactory(TestReporter)")); + case BEFORE_EACH, AFTER_EACH -> concatStreams(prefixed(allEvents, "test(TestReporter)"), + prefixed(allEvents, "testTemplate(TestReporter)[1]"), prefixed(allEvents, "testFactory(TestReporter)")); + case TEST_METHOD -> prefixed(allEvents, "test(TestReporter)"); + case TEST_TEMPLATE_METHOD -> prefixed(allEvents, "testTemplate(TestReporter)[1]"); + case TEST_FACTORY_METHOD -> prefixed(allEvents, "testFactory(TestReporter)"); + case DYNAMIC_TEST -> concatStreams(prefixed(beforeEvents, "testFactory(TestReporter)[1]"), + prefixed(testEvent, "testFactory(TestReporter)"), + prefixed(afterEvents, "testFactory(TestReporter)[1]")); + }; + + assertThat(getEvents(results, invocationType)) // + .containsExactlyElementsOf(expectedElements.toList()); + } + + @SafeVarargs + @SuppressWarnings("varargs") + private static Stream concatStreams(Stream... items) { + return Stream.of(items).flatMap(identity()); + } + + private static Stream prefixed(List values, String prefix) { + return prefixed(values, __ -> prefix); + } + + private static Stream prefixed(List values, UnaryOperator prefixGenerator) { + return values.stream() // + .map(it -> "[%s] %s".formatted(prefixGenerator.apply(it), it)); + } + + private Stream getEvents(EngineExecutionResults results, InvocationType invocationType) { + return results.allEvents().reportingEntryPublished().stream() // + .flatMap(event -> { + var reportEntry = event.getPayload(ReportEntry.class).orElseThrow(); + var keyValuePairs = reportEntry.getKeyValuePairs(); + if (keyValuePairs.keySet().stream() // + .map(InvocationType::valueOf) // + .anyMatch(isEqual(invocationType))) { + return keyValuePairs.values().stream() // + .map(it -> "[%s] %s".formatted(event.getTestDescriptor().getLegacyReportingName(), it)); + } + return Stream.empty(); + }); } @SuppressWarnings("JUnitMalformedDeclaration") @ExtendWith({ FooInvocationInterceptor.class, BarInvocationInterceptor.class, BazInvocationInterceptor.class }) + @TestMethodOrder(MethodOrderer.OrderAnnotation.class) static class TestCaseWithThreeInterceptors { public TestCaseWithThreeInterceptors(TestReporter reporter) { @@ -169,16 +223,19 @@ void beforeEach(TestReporter reporter) { publish(reporter, InvocationType.BEFORE_EACH); } + @Order(1) @Test void test(TestReporter reporter) { publish(reporter, InvocationType.TEST_METHOD); } + @Order(2) @RepeatedTest(1) void testTemplate(TestReporter reporter) { publish(reporter, InvocationType.TEST_TEMPLATE_METHOD); } + @Order(3) @TestFactory DynamicTest testFactory(TestReporter reporter) { publish(reporter, InvocationType.TEST_FACTORY_METHOD); @@ -224,6 +281,11 @@ abstract static class ReportingInvocationInterceptor implements InvocationInterc this.name = name; } + @Override + public ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) { + return ExtensionContextScope.TEST_METHOD; + } + @Override public void interceptBeforeAllMethod(Invocation invocation, ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) @@ -299,8 +361,8 @@ public void interceptDynamicTest(Invocation invocation, DynamicTestInvocat assertThat(invocationContext.getExecutable()).isNotNull(); assertThat(extensionContext.getUniqueId()).isNotBlank(); assertThat(extensionContext.getElement()).isEmpty(); - assertThat(extensionContext.getParent().flatMap(ExtensionContext::getTestMethod)).contains( - testClass.getDeclaredMethod("testFactory", TestReporter.class)); + assertThat(extensionContext.getParent().flatMap(ExtensionContext::getTestMethod)) // + .contains(testClass.getDeclaredMethod("testFactory", TestReporter.class)); reportAndProceed(invocation, extensionContext, InvocationType.DYNAMIC_TEST); } @@ -350,6 +412,12 @@ static class BarInvocationInterceptor extends ReportingInvocationInterceptor { BarInvocationInterceptor() { super("bar"); } + + @SuppressWarnings("deprecation") + @Override + public ExtensionContextScope getTestInstantiationExtensionContextScope(ExtensionContext rootContext) { + return ExtensionContextScope.DEFAULT; + } } static class BazInvocationInterceptor extends ReportingInvocationInterceptor {