/*
* Copyright 2015-2017 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 v1.0 which
* accompanies this distribution and is available at
*
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.junit.jupiter.engine.extension;
import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.AfterTestExecutionCallback;
import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.TestExtensionContext;
import org.junit.jupiter.engine.AbstractJupiterTestEngineTests;
import org.junit.jupiter.engine.JupiterTestEngine;
import org.junit.platform.engine.test.event.ExecutionEventRecorder;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
/**
* Integration tests that verify support for {@link BeforeTestExecutionCallback},
* {@link AfterTestExecutionCallback}, {@link BeforeEach}, and {@link AfterEach}
* in the {@link JupiterTestEngine}.
*
* @since 5.0
* @see BeforeAndAfterEachTests
*/
class BeforeAndAfterTestExecutionCallbackTests extends AbstractJupiterTestEngineTests {
private static List<String> callSequence = new ArrayList<>();
private static Optional<Throwable> actualExceptionInAfterTestExecution;
@BeforeEach
void resetCallSequence() {
callSequence.clear();
actualExceptionInAfterTestExecution = null;
}
@Test
void beforeAndAfterTestExecutionCallbacks() {
LauncherDiscoveryRequest request = request().selectors(selectClass(OuterTestCase.class)).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertEquals(2, eventRecorder.getTestStartedCount(), "# tests started");
assertEquals(2, eventRecorder.getTestSuccessfulCount(), "# tests succeeded");
assertEquals(0, eventRecorder.getTestSkippedCount(), "# tests skipped");
assertEquals(0, eventRecorder.getTestAbortedCount(), "# tests aborted");
assertEquals(0, eventRecorder.getTestFailedCount(), "# tests failed");
// @formatter:off
assertEquals(asList(
// OuterTestCase
"beforeEachMethodOuter",
"fooBeforeTestExecutionCallback",
"barBeforeTestExecutionCallback",
"testOuter",
"barAfterTestExecutionCallback",
"fooAfterTestExecutionCallback",
"afterEachMethodOuter",
// InnerTestCase
"beforeEachMethodOuter",
"beforeEachMethodInner",
"fooBeforeTestExecutionCallback",
"barBeforeTestExecutionCallback",
"fizzBeforeTestExecutionCallback",
"testInner",
"fizzAfterTestExecutionCallback",
"barAfterTestExecutionCallback",
"fooAfterTestExecutionCallback",
"afterEachMethodInner",
"afterEachMethodOuter"
), callSequence, "wrong call sequence");
// @formatter:on
}
@Test
void beforeAndAfterTestExecutionCallbacksDeclaredOnSuperclassAndSubclass() {
LauncherDiscoveryRequest request = request().selectors(selectClass(ChildTestCase.class)).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertEquals(1, eventRecorder.getTestStartedCount(), "# tests started");
assertEquals(1, eventRecorder.getTestSuccessfulCount(), "# tests succeeded");
assertEquals(0, eventRecorder.getTestSkippedCount(), "# tests skipped");
assertEquals(0, eventRecorder.getTestAbortedCount(), "# tests aborted");
assertEquals(0, eventRecorder.getTestFailedCount(), "# tests failed");
// @formatter:off
assertEquals(asList(
"fooBeforeTestExecutionCallback",
"barBeforeTestExecutionCallback",
"testChild",
"barAfterTestExecutionCallback",
"fooAfterTestExecutionCallback"
), callSequence, "wrong call sequence");
// @formatter:on
}
@Test
void beforeAndAfterTestExecutionCallbacksDeclaredOnInterfaceAndClass() {
LauncherDiscoveryRequest request = request().selectors(selectClass(TestInterfaceTestCase.class)).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertEquals(2, eventRecorder.getTestStartedCount(), "# tests started");
assertEquals(2, eventRecorder.getTestSuccessfulCount(), "# tests succeeded");
assertEquals(0, eventRecorder.getTestSkippedCount(), "# tests skipped");
assertEquals(0, eventRecorder.getTestAbortedCount(), "# tests aborted");
assertEquals(0, eventRecorder.getTestFailedCount(), "# tests failed");
// @formatter:off
assertEquals(asList(
// Test Interface
"fooBeforeTestExecutionCallback",
"barBeforeTestExecutionCallback",
"defaultTestMethod",
"barAfterTestExecutionCallback",
"fooAfterTestExecutionCallback",
// Test Class
"fooBeforeTestExecutionCallback",
"barBeforeTestExecutionCallback",
"localTestMethod",
"barAfterTestExecutionCallback",
"fooAfterTestExecutionCallback"
), callSequence, "wrong call sequence");
// @formatter:on
}
@Test
void beforeEachMethodThrowsAnException() {
LauncherDiscoveryRequest request = request().selectors(
selectClass(ExceptionInBeforeEachMethodTestCase.class)).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertEquals(1, eventRecorder.getTestStartedCount(), "# tests started");
assertEquals(0, eventRecorder.getTestSuccessfulCount(), "# tests succeeded");
assertEquals(0, eventRecorder.getTestSkippedCount(), "# tests skipped");
assertEquals(0, eventRecorder.getTestAbortedCount(), "# tests aborted");
assertEquals(1, eventRecorder.getTestFailedCount(), "# tests failed");
// @formatter:off
assertEquals(asList(
"beforeEachMethod", // throws an exception.
// fooBeforeTestExecutionCallback should not get invoked.
// test should not get invoked.
// fooAfterTestExecutionCallback should not get invoked.
"afterEachMethod"
), callSequence, "wrong call sequence");
// @formatter:on
assertNull(actualExceptionInAfterTestExecution,
"test exception (fooAfterTestExecutionCallback should not have been called)");
}
@Test
void beforeTestExecutionCallbackThrowsAnException() {
LauncherDiscoveryRequest request = request().selectors(
selectClass(ExceptionInBeforeTestExecutionCallbackTestCase.class)).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertEquals(1, eventRecorder.getTestStartedCount(), "# tests started");
assertEquals(0, eventRecorder.getTestSuccessfulCount(), "# tests succeeded");
assertEquals(0, eventRecorder.getTestSkippedCount(), "# tests skipped");
assertEquals(0, eventRecorder.getTestAbortedCount(), "# tests aborted");
assertEquals(1, eventRecorder.getTestFailedCount(), "# tests failed");
// @formatter:off
assertEquals(asList(
"beforeEachMethod",
"fooBeforeTestExecutionCallback",
"exceptionThrowingBeforeTestExecutionCallback", // throws an exception.
// barBeforeTestExecutionCallback should not get invoked.
// test() should not get invoked.
"barAfterTestExecutionCallback",
"fooAfterTestExecutionCallback",
"afterEachMethod"
), callSequence, "wrong call sequence");
// @formatter:on
assertNotNull(actualExceptionInAfterTestExecution, "test exception");
assertTrue(actualExceptionInAfterTestExecution.isPresent(), "test exception should be present");
assertEquals(EnigmaException.class, actualExceptionInAfterTestExecution.get().getClass());
}
@Test
void afterTestExecutionCallbackThrowsAnException() {
LauncherDiscoveryRequest request = request().selectors(
selectClass(ExceptionInAfterTestExecutionCallbackTestCase.class)).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertEquals(1, eventRecorder.getTestStartedCount(), "# tests started");
assertEquals(0, eventRecorder.getTestSuccessfulCount(), "# tests succeeded");
assertEquals(0, eventRecorder.getTestSkippedCount(), "# tests skipped");
assertEquals(0, eventRecorder.getTestAbortedCount(), "# tests aborted");
assertEquals(1, eventRecorder.getTestFailedCount(), "# tests failed");
// @formatter:off
assertEquals(asList(
"beforeEachMethod",
"fooBeforeTestExecutionCallback",
"barBeforeTestExecutionCallback",
"test",
"barAfterTestExecutionCallback",
"exceptionThrowingAfterTestExecutionCallback", // throws an exception.
"fooAfterTestExecutionCallback",
"afterEachMethod"
), callSequence, "wrong call sequence");
// @formatter:on
assertNotNull(actualExceptionInAfterTestExecution, "test exception");
assertTrue(actualExceptionInAfterTestExecution.isPresent(), "test exception should be present");
assertEquals(EnigmaException.class, actualExceptionInAfterTestExecution.get().getClass());
}
@Test
void testMethodThrowsAnException() {
LauncherDiscoveryRequest request = request().selectors(
selectClass(ExceptionInTestMethodTestCase.class)).build();
ExecutionEventRecorder eventRecorder = executeTests(request);
assertEquals(1, eventRecorder.getTestStartedCount(), "# tests started");
assertEquals(0, eventRecorder.getTestSuccessfulCount(), "# tests succeeded");
assertEquals(0, eventRecorder.getTestSkippedCount(), "# tests skipped");
assertEquals(0, eventRecorder.getTestAbortedCount(), "# tests aborted");
assertEquals(1, eventRecorder.getTestFailedCount(), "# tests failed");
// @formatter:off
assertEquals(asList(
"beforeEachMethod",
"fooBeforeTestExecutionCallback",
"test", // throws an exception.
"fooAfterTestExecutionCallback",
"afterEachMethod"
), callSequence, "wrong call sequence");
// @formatter:on
assertNotNull(actualExceptionInAfterTestExecution, "test exception");
assertTrue(actualExceptionInAfterTestExecution.isPresent(), "test exception should be present");
assertEquals(EnigmaException.class, actualExceptionInAfterTestExecution.get().getClass());
}
// -------------------------------------------------------------------------
@ExtendWith(FooTestExecutionCallbacks.class)
private static class ParentTestCase {
}
@ExtendWith(BarTestExecutionCallbacks.class)
private static class ChildTestCase extends ParentTestCase {
@Test
void test() {
callSequence.add("testChild");
}
}
@ExtendWith(FooTestExecutionCallbacks.class)
private interface TestInterface {
@Test
default void defaultTest() {
callSequence.add("defaultTestMethod");
}
}
@ExtendWith(BarTestExecutionCallbacks.class)
private static class TestInterfaceTestCase implements TestInterface {
@Test
void localTest() {
callSequence.add("localTestMethod");
}
}
@ExtendWith({ FooTestExecutionCallbacks.class, BarTestExecutionCallbacks.class })
private static class OuterTestCase {
@BeforeEach
void beforeEach() {
callSequence.add("beforeEachMethodOuter");
}
@Test
void testOuter() {
callSequence.add("testOuter");
}
@AfterEach
void afterEach() {
callSequence.add("afterEachMethodOuter");
}
@Nested
@ExtendWith(FizzTestExecutionCallbacks.class)
class InnerTestCase {
@BeforeEach
void beforeInnerMethod() {
callSequence.add("beforeEachMethodInner");
}
@Test
void testInner() {
callSequence.add("testInner");
}
@AfterEach
void afterInnerMethod() {
callSequence.add("afterEachMethodInner");
}
}
}
@ExtendWith({ FooTestExecutionCallbacks.class, ExceptionThrowingBeforeTestExecutionCallback.class,
BarTestExecutionCallbacks.class })
private static class ExceptionInBeforeTestExecutionCallbackTestCase {
@BeforeEach
void beforeEach() {
callSequence.add("beforeEachMethod");
}
@Test
void test() {
callSequence.add("test");
}
@AfterEach
void afterEach() {
callSequence.add("afterEachMethod");
}
}
@ExtendWith({ FooTestExecutionCallbacks.class, ExceptionThrowingAfterTestExecutionCallback.class,
BarTestExecutionCallbacks.class })
private static class ExceptionInAfterTestExecutionCallbackTestCase {
@BeforeEach
void beforeEach() {
callSequence.add("beforeEachMethod");
}
@Test
void test() {
callSequence.add("test");
}
@AfterEach
void afterEach() {
callSequence.add("afterEachMethod");
}
}
@ExtendWith(FooTestExecutionCallbacks.class)
private static class ExceptionInBeforeEachMethodTestCase {
@BeforeEach
void beforeEach() {
callSequence.add("beforeEachMethod");
throw new EnigmaException("@BeforeEach");
}
@Test
void test() {
callSequence.add("test");
}
@AfterEach
void afterEach() {
callSequence.add("afterEachMethod");
}
}
@ExtendWith(FooTestExecutionCallbacks.class)
private static class ExceptionInTestMethodTestCase {
@BeforeEach
void beforeEach() {
callSequence.add("beforeEachMethod");
}
@Test
void test() {
callSequence.add("test");
throw new EnigmaException("@Test");
}
@AfterEach
void afterEach() {
callSequence.add("afterEachMethod");
}
}
// -------------------------------------------------------------------------
private static class FooTestExecutionCallbacks implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
@Override
public void beforeTestExecution(TestExtensionContext context) {
callSequence.add("fooBeforeTestExecutionCallback");
}
@Override
public void afterTestExecution(TestExtensionContext context) {
callSequence.add("fooAfterTestExecutionCallback");
actualExceptionInAfterTestExecution = context.getTestException();
}
}
private static class BarTestExecutionCallbacks implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
@Override
public void beforeTestExecution(TestExtensionContext context) {
callSequence.add("barBeforeTestExecutionCallback");
}
@Override
public void afterTestExecution(TestExtensionContext context) {
callSequence.add("barAfterTestExecutionCallback");
}
}
private static class FizzTestExecutionCallbacks implements BeforeTestExecutionCallback, AfterTestExecutionCallback {
@Override
public void beforeTestExecution(TestExtensionContext context) {
callSequence.add("fizzBeforeTestExecutionCallback");
}
@Override
public void afterTestExecution(TestExtensionContext context) {
callSequence.add("fizzAfterTestExecutionCallback");
}
}
private static class ExceptionThrowingBeforeTestExecutionCallback implements BeforeTestExecutionCallback {
@Override
public void beforeTestExecution(TestExtensionContext context) {
callSequence.add("exceptionThrowingBeforeTestExecutionCallback");
throw new EnigmaException("BeforeTestExecutionCallback");
}
}
private static class ExceptionThrowingAfterTestExecutionCallback implements AfterTestExecutionCallback {
@Override
public void afterTestExecution(TestExtensionContext context) {
callSequence.add("exceptionThrowingAfterTestExecutionCallback");
throw new EnigmaException("AfterTestExecutionCallback");
}
}
@SuppressWarnings("serial")
private static class EnigmaException extends RuntimeException {
EnigmaException(String message) {
super(message);
}
}
}