package lab.jm.fixtures; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInfo; import org.junit.jupiter.api.extension.AfterAllCallback; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.AfterTestExecutionCallback; import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; import org.junit.jupiter.api.extension.BeforeTestExecutionCallback; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.extension.ParameterResolver; import org.junit.jupiter.api.extension.RegisterExtension; public final class ExtensionFixtures { private ExtensionFixtures() { } /** Logs every callback it receives, tagged with a label. */ public static class Tracer implements BeforeAllCallback, BeforeEachCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback, AfterEachCallback, AfterAllCallback { private final String label; public Tracer() { this("declared"); } public Tracer(String label) { this.label = label; } @Override public void beforeAll(ExtensionContext c) { Log.add(label + ": beforeAll"); } @Override public void beforeEach(ExtensionContext c) { Log.add(label + ": beforeEach"); } @Override public void beforeTestExecution(ExtensionContext c) { Log.add(label + ": beforeTestExecution"); } @Override public void afterTestExecution(ExtensionContext c) { Log.add(label + ": afterTestExecution"); } @Override public void afterEach(ExtensionContext c) { Log.add(label + ": afterEach"); } @Override public void afterAll(ExtensionContext c) { Log.add(label + ": afterAll"); } } /** One declarative extension, to show the callback order around a test and its @BeforeEach method. */ @ExtendWith(Tracer.class) public static class CallbackOrder { @BeforeEach void beforeEachMethod() { Log.add("test class: @BeforeEach method"); } @Test void theTest() { Log.add("test class: the test body"); } } /** Declarative (@ExtendWith) and programmatic (@RegisterExtension) side by side. */ @ExtendWith(Tracer.class) public static class Registration { @RegisterExtension static final Tracer PROGRAMMATIC = new Tracer("programmatic(static field)"); @Test void theTest() { Log.add("test body"); } } /** A value the custom resolver hands to tests. */ public record Tenant(String name) { } /** Resolves Tenant parameters, in constructors and in test methods alike. */ public static class TenantResolver implements ParameterResolver { @Override public boolean supportsParameter(ParameterContext p, ExtensionContext c) { return p.getParameter().getType() == Tenant.class; } @Override public Object resolveParameter(ParameterContext p, ExtensionContext c) { return new Tenant("tenant-for-" + c.getRequiredTestMethod().getName()); } } @ExtendWith(TenantResolver.class) public static class Injection { @Test void resolvesCustomAndBuiltIn(Tenant tenant, TestInfo info) { Log.add("custom parameter : " + tenant); Log.add("built-in TestInfo: displayName=" + info.getDisplayName()); } } }