Skip to content

Commit

Permalink
Add @testinstance(Lifecycle.PER_CLASS) support for @nested test classes
Browse files Browse the repository at this point in the history
Issue: #419
  • Loading branch information
sbrannen committed Jun 30, 2017
1 parent 7205f05 commit 6f4244f
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public class ClassTestDescriptor extends JupiterTestDescriptor {
private static final ExecutableInvoker executableInvoker = new ExecutableInvoker();

private final Class<?> testClass;
private final Lifecycle lifecycle;
protected final Lifecycle lifecycle;

private final List<Method> beforeAllMethods;
private final List<Method> afterAllMethods;
Expand Down Expand Up @@ -179,7 +179,15 @@ public void after(JupiterEngineExecutionContext context) throws Exception {
protected TestInstanceProvider testInstanceProvider(JupiterEngineExecutionContext parentExecutionContext,
ExtensionRegistry registry, ExtensionContext extensionContext) {

return new LifecycleAwareTestInstanceProvider(this.testClass, registry, extensionContext);
TestInstanceProvider testInstanceProvider = childExtensionRegistry -> {
Constructor<?> constructor = ReflectionUtils.getDeclaredConstructor(this.testClass);
Object instance = executableInvoker.invoke(constructor, extensionContext,
childExtensionRegistry.orElse(registry));
invokeTestInstancePostProcessors(instance, childExtensionRegistry.orElse(registry), extensionContext);
return instance;
};

return new LifecycleAwareDelegatingTestInstanceProvider(testInstanceProvider, this.lifecycle);
}

protected void invokeTestInstancePostProcessors(Object instance, ExtensionRegistry registry,
Expand Down Expand Up @@ -281,43 +289,29 @@ private void invokeMethodInTestExtensionContext(Method method, TestExtensionCont
executableInvoker.invoke(method, instance, context, registry);
}

private final class LifecycleAwareTestInstanceProvider implements TestInstanceProvider {
protected static final class LifecycleAwareDelegatingTestInstanceProvider implements TestInstanceProvider {

private final Class<?> testClass;
private final ExtensionRegistry registry;
private final ExtensionContext extensionContext;
private final TestInstanceProvider testInstanceProvider;
private final Lifecycle lifecycle;
private Object testInstance;

LifecycleAwareTestInstanceProvider(Class<?> testClass, ExtensionRegistry registry,
ExtensionContext extensionContext) {

this.testClass = testClass;
this.registry = registry;
this.extensionContext = extensionContext;
LifecycleAwareDelegatingTestInstanceProvider(TestInstanceProvider testInstanceProvider, Lifecycle lifecycle) {
this.testInstanceProvider = testInstanceProvider;
this.lifecycle = lifecycle;
}

@Override
public Object getTestInstance(Optional<ExtensionRegistry> childExtensionRegistry) {
if (ClassTestDescriptor.this.lifecycle == Lifecycle.PER_METHOD) {
return createTestInstance(childExtensionRegistry);
if (this.lifecycle == Lifecycle.PER_METHOD) {
return this.testInstanceProvider.getTestInstance(childExtensionRegistry);
}

// else Lifecycle.PER_CLASS
if (this.testInstance == null) {
this.testInstance = createTestInstance(childExtensionRegistry);
this.testInstance = this.testInstanceProvider.getTestInstance(childExtensionRegistry);
}
return this.testInstance;
}

private Object createTestInstance(Optional<ExtensionRegistry> childExtensionRegistry) {
Constructor<?> constructor = ReflectionUtils.getDeclaredConstructor(this.testClass);
Object instance = executableInvoker.invoke(constructor, this.extensionContext,
childExtensionRegistry.orElse(this.registry));
invokeTestInstancePostProcessors(instance, childExtensionRegistry.orElse(this.registry),
this.extensionContext);
return instance;
}

}

private static TestInstance.Lifecycle getTestInstanceLifecycle(Class<?> testClass) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,24 @@ public final Set<TestTag> getTags() {
protected TestInstanceProvider testInstanceProvider(JupiterEngineExecutionContext parentExecutionContext,
ExtensionRegistry registry, ExtensionContext extensionContext) {

return childExtensionRegistry -> {
TestInstanceProvider testInstanceProvider = childExtensionRegistry -> {
// Extensions registered for nested classes and below are not to be used for instantiating outer classes
Optional<ExtensionRegistry> childExtensionRegistryForOuterInstance = Optional.empty();
Object outerInstance = parentExecutionContext.getTestInstanceProvider().getTestInstance(
childExtensionRegistryForOuterInstance);

// @formatter:off
Object outerInstance = parentExecutionContext.getTestInstanceProvider().getTestInstance(childExtensionRegistryForOuterInstance);

ExtensionRegistry registryToUse = childExtensionRegistry.orElse(registry);
Constructor<?> constructor = ReflectionUtils.getDeclaredConstructor(getTestClass());
Object instance = executableInvoker.invoke(constructor, outerInstance, extensionContext,
childExtensionRegistry.orElse(registry));
invokeTestInstancePostProcessors(instance, childExtensionRegistry.orElse(registry), extensionContext);
Object instance = executableInvoker.invoke(constructor, outerInstance, extensionContext, registryToUse);

invokeTestInstancePostProcessors(instance, registryToUse, extensionContext);

return instance;
// @formatter:on
};

return new LifecycleAwareDelegatingTestInstanceProvider(testInstanceProvider, this.lifecycle);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,22 @@
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.platform.engine.test.event.ExecutionEventRecorder;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;

/**
* Tests for {@link TestInstance @TestInstance} lifecycle support.
*
* @since 5.0
*/
@RunWith(JUnitPlatform.class)
public

class TestInstanceLifecycleTests extends AbstractJupiterTestEngineTests {

private static int instanceCount;
private static int nestedInstanceCount;
private static int beforeAllCount;
private static int afterAllCount;
private static int beforeEachCount;
Expand All @@ -45,6 +42,7 @@ class TestInstanceLifecycleTests extends AbstractJupiterTestEngineTests {
@BeforeEach
void init() {
instanceCount = 0;
nestedInstanceCount = 0;
beforeAllCount = 0;
afterAllCount = 0;
beforeEachCount = 0;
Expand All @@ -53,36 +51,47 @@ void init() {

@Test
void instancePerMethod() {
performAssertions(InstancePerMethodTestCase.class, 2, 1, 1);
performAssertions(InstancePerMethodTestCase.class, 2, 2, 0, 1);
}

@Test
void instancePerClass() {
performAssertions(InstancePerClassTestCase.class, 1, 2, 2);
performAssertions(InstancePerClassTestCase.class, 2, 1, 0, 2);
}

private void performAssertions(Class<?> testClass, int expectedInstanceCount, int expectedBeforeAllCount,
int expectedAfterAllCount) {
@Test
void instancePerMethodWithNestedTestClass() {
performAssertions(InstancePerMethodOuterTestCase.class, 3, 2, 2, 0);
}

ExecutionEventRecorder eventRecorder = executeTestsForClass(testClass);
@Test
void instancePerClassWithNestedTestClass() {
performAssertions(InstancePerClassOuterTestCase.class, 3, 1, 1, 1);
}

// eventRecorder.eventStream().forEach(System.out::println);
private void performAssertions(Class<?> testClass, int containers, int instances, int nestedInstances,
int allMethods) {

ExecutionEventRecorder eventRecorder = executeTestsForClass(testClass);

// @formatter:off
assertAll(
() -> assertEquals(2, eventRecorder.getContainerStartedCount(), "# containers started"),
() -> assertEquals(2, eventRecorder.getContainerFinishedCount(), "# containers finished"),
() -> assertEquals(containers, eventRecorder.getContainerStartedCount(), "# containers started"),
() -> assertEquals(containers, eventRecorder.getContainerFinishedCount(), "# containers finished"),
() -> assertEquals(2, eventRecorder.getTestStartedCount(), "# tests started"),
() -> assertEquals(2, eventRecorder.getTestSuccessfulCount(), "# tests succeeded"),
() -> assertEquals(expectedInstanceCount, instanceCount, "instance count"),
() -> assertEquals(expectedBeforeAllCount, beforeAllCount, "@BeforeAll count"),
() -> assertEquals(expectedAfterAllCount, afterAllCount, "@AfterAll count"),
() -> assertEquals(instances, instanceCount, "instance count"),
() -> assertEquals(nestedInstances, nestedInstanceCount, "nested instance count"),
() -> assertEquals(allMethods, beforeAllCount, "@BeforeAll count"),
() -> assertEquals(allMethods, afterAllCount, "@AfterAll count"),
() -> assertEquals(2, beforeEachCount, "@BeforeEach count"),
() -> assertEquals(2, afterEachCount, "@AfterEach count")
);
// @formatter:on
}

// -------------------------------------------------------------------------

// The following is commented out b/c it's the default.
// @TestInstance(Lifecycle.PER_METHOD)
private static class InstancePerMethodTestCase {
Expand Down Expand Up @@ -140,4 +149,92 @@ void afterAll(TestInfo testInfo) {

}

// The following is commented out b/c it's the default.
// @TestInstance(Lifecycle.PER_METHOD)
private static class InstancePerMethodOuterTestCase {

@SuppressWarnings("unused")
InstancePerMethodOuterTestCase() {
instanceCount++;
}

@Nested
// The following is commented out b/c it's the default.
// @TestInstance(Lifecycle.PER_METHOD)
class NestedInstancePerMethodTestCase {

@SuppressWarnings("unused")
NestedInstancePerMethodTestCase() {
nestedInstanceCount++;
}

@BeforeEach
void beforeEach() {
beforeEachCount++;
}

@Test
void test1() {
}

@Test
void test2() {
}

@AfterEach
void afterEach() {
afterEachCount++;
}
}
}

@TestInstance(Lifecycle.PER_CLASS)
private static class InstancePerClassOuterTestCase {

@SuppressWarnings("unused")
InstancePerClassOuterTestCase() {
instanceCount++;
}

@Nested
@TestInstance(Lifecycle.PER_CLASS)
class NestedInstancePerClassTestCase {

@SuppressWarnings("unused")
NestedInstancePerClassTestCase() {
nestedInstanceCount++;
}

@BeforeAll
void beforeAll(TestInfo testInfo) {
assertNotNull(testInfo);
beforeAllCount++;
}

@BeforeEach
void beforeEach() {
beforeEachCount++;
}

@Test
void test1() {
}

@Test
void test2() {
}

@AfterEach
void afterEach() {
afterEachCount++;
}

@AfterAll
void afterAll(TestInfo testInfo) {
assertNotNull(testInfo);
afterAllCount++;
}
}
}

}

0 comments on commit 6f4244f

Please sign in to comment.