Skip to content

Commit

Permalink
Add tests for extension context scope supplied to InvocationInterceptor
Browse files Browse the repository at this point in the history
  • Loading branch information
marcphilipp committed Oct 11, 2024
1 parent b083686 commit a6230df
Showing 1 changed file with 85 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -22,17 +24,23 @@

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;
import org.junit.jupiter.api.AfterEach;
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;
Expand All @@ -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;
Expand Down Expand Up @@ -130,28 +136,76 @@ void test() {
}
}

@ParameterizedTest(name = "{0}")
@EnumSource(InvocationType.class)
void callsInterceptors(InvocationType invocationType) {
@TestFactory
Stream<DynamicTest> 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<String> getEvents(EngineExecutionResults results, EnumSet<InvocationType> 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 <T> Stream<T> concatStreams(Stream<T>... items) {
return Stream.of(items).flatMap(identity());
}

private static Stream<String> prefixed(List<String> values, String prefix) {
return prefixed(values, __ -> prefix);
}

private static Stream<String> prefixed(List<String> values, UnaryOperator<String> prefixGenerator) {
return values.stream() //
.map(it -> "[%s] %s".formatted(prefixGenerator.apply(it), it));
}

private Stream<String> 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) {
Expand All @@ -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);
Expand Down Expand Up @@ -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<Void> invocation,
ReflectiveInvocationContext<Method> invocationContext, ExtensionContext extensionContext)
Expand Down Expand Up @@ -299,8 +361,8 @@ public void interceptDynamicTest(Invocation<Void> 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);
}

Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit a6230df

Please sign in to comment.