blob: 87114faf899472abb970e320299701c5017f9bf0 [file]
/*
* Copyright 2015-2022 the original author or authors.
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v2.0 which
* accompanies this distribution and is available at
*
* https://www.eclipse.org/legal/epl-v20.html
*/
package org.junit.jupiter.engine;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.util.Throwables.getRootCause;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectMethod;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectUniqueId;
import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.engine.NestedTestClassesTests.OuterClass.NestedClass;
import org.junit.jupiter.engine.NestedTestClassesTests.OuterClass.NestedClass.RecursiveNestedClass;
import org.junit.jupiter.engine.NestedTestClassesTests.OuterClass.NestedClass.RecursiveNestedSiblingClass;
import org.junit.platform.commons.JUnitException;
import org.junit.platform.engine.TestDescriptor;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.testkit.engine.EngineExecutionResults;
import org.junit.platform.testkit.engine.Events;
/**
* Integration tests that verify support for {@linkplain Nested nested contexts}
* in the {@link JupiterTestEngine}.
*
* @since 5.0
*/
class NestedTestClassesTests extends AbstractJupiterTestEngineTests {
@Test
void nestedTestsAreCorrectlyDiscovered() {
LauncherDiscoveryRequest request = request().selectors(selectClass(TestCaseWithNesting.class)).build();
TestDescriptor engineDescriptor = discoverTests(request);
assertEquals(5, engineDescriptor.getDescendants().size(), "# resolved test descriptors");
}
@Test
void nestedTestsAreExecuted() {
EngineExecutionResults executionResults = executeTestsForClass(TestCaseWithNesting.class);
Events containers = executionResults.containerEvents();
Events tests = executionResults.testEvents();
assertEquals(3, tests.started().count(), "# tests started");
assertEquals(2, tests.succeeded().count(), "# tests succeeded");
assertEquals(1, tests.failed().count(), "# tests failed");
assertEquals(3, containers.started().count(), "# containers started");
assertEquals(3, containers.finished().count(), "# containers finished");
}
@Test
void doublyNestedTestsAreCorrectlyDiscovered() {
LauncherDiscoveryRequest request = request().selectors(selectClass(TestCaseWithDoubleNesting.class)).build();
TestDescriptor engineDescriptor = discoverTests(request);
assertEquals(8, engineDescriptor.getDescendants().size(), "# resolved test descriptors");
}
@Test
void doublyNestedTestsAreExecuted() {
EngineExecutionResults executionResults = executeTestsForClass(TestCaseWithDoubleNesting.class);
Events containers = executionResults.containerEvents();
Events tests = executionResults.testEvents();
assertEquals(5, tests.started().count(), "# tests started");
assertEquals(3, tests.succeeded().count(), "# tests succeeded");
assertEquals(2, tests.failed().count(), "# tests failed");
assertEquals(4, containers.started().count(), "# containers started");
assertEquals(4, containers.finished().count(), "# containers finished");
assertAll("before each counts", //
() -> assertEquals(5, TestCaseWithDoubleNesting.beforeTopCount),
() -> assertEquals(4, TestCaseWithDoubleNesting.beforeNestedCount),
() -> assertEquals(2, TestCaseWithDoubleNesting.beforeDoublyNestedCount));
assertAll("after each counts", //
() -> assertEquals(5, TestCaseWithDoubleNesting.afterTopCount),
() -> assertEquals(4, TestCaseWithDoubleNesting.afterNestedCount),
() -> assertEquals(2, TestCaseWithDoubleNesting.afterDoublyNestedCount));
}
@Test
void inheritedNestedTestsAreExecuted() {
EngineExecutionResults executionResults = executeTestsForClass(TestCaseWithInheritedNested.class);
Events containers = executionResults.containerEvents();
Events tests = executionResults.testEvents();
assertEquals(3, tests.started().count(), "# tests started");
assertEquals(2, tests.succeeded().count(), "# tests succeeded");
assertEquals(1, tests.failed().count(), "# tests failed");
assertEquals(4, containers.started().count(), "# containers started");
assertEquals(4, containers.finished().count(), "# containers finished");
}
@Test
void extendedNestedTestsAreExecuted() {
EngineExecutionResults executionResults = executeTestsForClass(TestCaseWithExtendedNested.class);
executionResults.allEvents().debug();
Events containers = executionResults.containerEvents();
Events tests = executionResults.testEvents();
assertEquals(6, tests.started().count(), "# tests started");
assertEquals(4, tests.succeeded().count(), "# tests succeeded");
assertEquals(2, tests.failed().count(), "# tests failed");
assertEquals(8, containers.started().count(), "# containers started");
assertEquals(8, containers.finished().count(), "# containers finished");
}
@Test
void deeplyNestedInheritedMethodsAreExecutedWhenSelectedViaUniqueId() {
EngineExecutionResults executionResults = executeTests(selectUniqueId(
"[engine:junit-jupiter]/[class:org.junit.jupiter.engine.NestedTestClassesTests$TestCaseWithExtendedNested]/[nested-class:ConcreteInner1]/[nested-class:NestedInAbstractClass]/[nested-class:SecondLevelInherited]/[method:test()]"),
selectUniqueId(
"[engine:junit-jupiter]/[class:org.junit.jupiter.engine.NestedTestClassesTests$TestCaseWithExtendedNested]/[nested-class:ConcreteInner2]/[nested-class:NestedInAbstractClass]/[nested-class:SecondLevelInherited]/[method:test()]"));
executionResults.allEvents().debug();
Events containers = executionResults.containerEvents();
Events tests = executionResults.testEvents();
assertEquals(2, tests.started().count(), "# tests started");
assertEquals(2, tests.succeeded().count(), "# tests succeeded");
assertEquals(0, tests.failed().count(), "# tests failed");
assertEquals(8, containers.started().count(), "# containers started");
assertEquals(8, containers.finished().count(), "# containers finished");
}
/**
* @since 1.6
*/
@Test
void recursiveNestedTestClassHierarchiesAreNotExecuted() {
assertNestedCycle(OuterClass.class, RecursiveNestedClass.class, OuterClass.class);
assertNestedCycle(NestedClass.class, RecursiveNestedClass.class, OuterClass.class);
assertNestedCycle(RecursiveNestedClass.class, RecursiveNestedClass.class, OuterClass.class);
}
/**
* NOTE: We do not actually support this as a feature, but we currently only
* check for cycles if a class is selected. Thus, the tests in this method
* pass, since the selection of a particular method does not result in a
* lookup for nested test classes.
*
* @since 1.6
*/
@Test
void individualMethodsWithinRecursiveNestedTestClassHierarchiesAreExecuted() {
EngineExecutionResults executionResults = executeTests(selectMethod(OuterClass.class, "outer"));
executionResults.containerEvents().assertStatistics(stats -> stats.started(2).succeeded(2));
executionResults.testEvents().assertStatistics(stats -> stats.started(1).succeeded(1));
executionResults = executeTests(selectMethod(NestedClass.class, "nested"));
executionResults.containerEvents().assertStatistics(stats -> stats.started(3).succeeded(3));
executionResults.testEvents().assertStatistics(stats -> stats.started(1).succeeded(1));
executionResults = executeTests(selectMethod(RecursiveNestedClass.class, "nested"));
executionResults.containerEvents().assertStatistics(stats -> stats.started(4).succeeded(4));
executionResults.testEvents().assertStatistics(stats -> stats.started(1).succeeded(1));
executionResults = executeTests(selectMethod(RecursiveNestedSiblingClass.class, "nested"));
executionResults.containerEvents().assertStatistics(stats -> stats.started(4).succeeded(4));
executionResults.testEvents().assertStatistics(stats -> stats.started(1).succeeded(1));
}
private void assertNestedCycle(Class<?> start, Class<?> from, Class<?> to) {
assertThatExceptionOfType(JUnitException.class)//
.isThrownBy(() -> executeTestsForClass(start))//
.withCauseExactlyInstanceOf(JUnitException.class)//
.satisfies(ex -> assertThat(getRootCause(ex)).hasMessageMatching(
String.format("Detected cycle in inner class hierarchy between .+%s and .+%s", from.getSimpleName(),
to.getSimpleName())));
}
// -------------------------------------------------------------------
static class TestCaseWithNesting {
@Test
void someTest() {
}
@Nested
class NestedTestCase {
@Test
void successful() {
}
@Test
void failing() {
Assertions.fail("Something went horribly wrong");
}
}
}
static class TestCaseWithDoubleNesting {
static int beforeTopCount = 0;
static int beforeNestedCount = 0;
static int beforeDoublyNestedCount = 0;
static int afterTopCount = 0;
static int afterNestedCount = 0;
static int afterDoublyNestedCount = 0;
@BeforeEach
void beforeTop() {
beforeTopCount++;
}
@AfterEach
void afterTop() {
afterTopCount++;
}
@Test
void someTest() {
}
@Nested
class NestedTestCase {
@BeforeEach
void beforeNested() {
beforeNestedCount++;
}
@AfterEach
void afterNested() {
afterNestedCount++;
}
@Test
void successful() {
}
@Test
void failing() {
Assertions.fail("Something went horribly wrong");
}
@Nested
class DoublyNestedTestCase {
@BeforeEach
void beforeDoublyNested() {
beforeDoublyNestedCount++;
}
@BeforeEach
void afterDoublyNested() {
afterDoublyNestedCount++;
}
@Test
void successful() {
}
@Test
void failing() {
Assertions.fail("Something went horribly wrong");
}
}
}
}
interface InterfaceWithNestedClass {
@Nested
class NestedInInterface {
@Test
void notExecutedByImplementingClass() {
Assertions.fail("class in interface is static and should have been filtered out");
}
}
}
static abstract class AbstractSuperClass implements InterfaceWithNestedClass {
@Nested
class NestedInAbstractClass {
@Test
void successful() {
}
@Test
void failing() {
Assertions.fail("something went wrong");
}
@Nested
class SecondLevelInherited {
@Test
void test() {
}
}
}
}
static class TestCaseWithInheritedNested extends AbstractSuperClass {
// empty on purpose
}
static class TestCaseWithExtendedNested {
@Nested
class ConcreteInner1 extends AbstractSuperClass {
}
@Nested
class ConcreteInner2 extends AbstractSuperClass {
}
}
static class AbstractOuterClass {
}
static class OuterClass extends AbstractOuterClass {
@Test
void outer() {
}
@Nested
class NestedClass {
@Test
void nested() {
}
@Nested
class RecursiveNestedClass extends OuterClass {
@Test
void nested() {
}
}
@Nested
// sibling of OuterClass due to common super type
class RecursiveNestedSiblingClass extends AbstractOuterClass {
@Test
void nested() {
}
}
}
}
}