/*
* 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 org.assertj.core.api.Assertions.allOf;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
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.test.event.ExecutionEventConditions.assertRecordedExecutionEventsContainsExactly;
import static org.junit.platform.engine.test.event.ExecutionEventConditions.event;
import static org.junit.platform.engine.test.event.ExecutionEventConditions.finishedWithFailure;
import static org.junit.platform.engine.test.event.ExecutionEventConditions.test;
import static org.junit.platform.engine.test.event.TestExecutionResultConditions.isA;
import static org.junit.platform.engine.test.event.TestExecutionResultConditions.message;
import java.util.function.Predicate;
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.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInfo;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
import org.junit.jupiter.engine.AbstractJupiterTestEngineTests;
import org.junit.jupiter.engine.JupiterTestEngine;
import org.junit.jupiter.engine.execution.injection.sample.CustomAnnotation;
import org.junit.jupiter.engine.execution.injection.sample.CustomAnnotationParameterResolver;
import org.junit.jupiter.engine.execution.injection.sample.CustomType;
import org.junit.jupiter.engine.execution.injection.sample.CustomTypeParameterResolver;
import org.junit.jupiter.engine.execution.injection.sample.NullIntegerParameterResolver;
import org.junit.jupiter.engine.execution.injection.sample.NumberParameterResolver;
import org.junit.jupiter.engine.execution.injection.sample.PrimitiveArrayParameterResolver;
import org.junit.jupiter.engine.execution.injection.sample.PrimitiveIntegerParameterResolver;
import org.junit.platform.engine.test.event.ExecutionEventRecorder;
/**
* Integration tests that verify support for {@link ParameterResolver}
* extensions in the {@link JupiterTestEngine}.
*
* @since 5.0
*/
class ParameterResolverTests extends AbstractJupiterTestEngineTests {
@Test
void constructorInjection() {
ExecutionEventRecorder eventRecorder = executeTestsForClass(ConstructorInjectionTestCase.class);
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");
}
@Test
void executeTestsForMethodInjectionCases() {
ExecutionEventRecorder eventRecorder = executeTestsForClass(MethodInjectionTestCase.class);
assertEquals(7, eventRecorder.getTestStartedCount(), "# tests started");
assertEquals(6, eventRecorder.getTestSuccessfulCount(), "# tests succeeded");
assertEquals(0, eventRecorder.getTestSkippedCount(), "# tests skipped");
assertEquals(0, eventRecorder.getTestAbortedCount(), "# tests aborted");
assertEquals(1, eventRecorder.getTestFailedCount(), "# tests failed");
}
@Test
void executeTestsForNullValuedMethodInjectionCases() {
ExecutionEventRecorder eventRecorder = executeTestsForClass(NullMethodInjectionTestCase.class);
assertEquals(2, eventRecorder.getTestStartedCount(), "# tests started");
assertEquals(1, eventRecorder.getTestSuccessfulCount(), "# tests succeeded");
assertEquals(1, eventRecorder.getTestFailedCount(), "# tests failed");
// @formatter:off
Predicate<String> expectations = s ->
s.contains("NullIntegerParameterResolver") &&
s.contains("resolved a null value for parameter") &&
s.contains("but a primitive of type [int] is required");
assertRecordedExecutionEventsContainsExactly(eventRecorder.getFailedTestFinishedEvents(),
event(
test("injectPrimitive"),
finishedWithFailure(allOf(isA(ParameterResolutionException.class), message(expectations)))
));
// @formatter:on
}
@Test
void executeTestsForPrimitiveIntegerMethodInjectionCases() {
ExecutionEventRecorder eventRecorder = executeTestsForClass(PrimitiveIntegerMethodInjectionTestCase.class);
assertEquals(1, eventRecorder.getTestStartedCount(), "# tests started");
assertEquals(1, eventRecorder.getTestSuccessfulCount(), "# tests succeeded");
assertEquals(0, eventRecorder.getTestFailedCount(), "# tests failed");
}
@Test
void executeTestsForPrimitiveArrayMethodInjectionCases() {
ExecutionEventRecorder eventRecorder = executeTestsForClass(PrimitiveArrayMethodInjectionTestCase.class);
assertEquals(1, eventRecorder.getTestStartedCount(), "# tests started");
assertEquals(1, eventRecorder.getTestSuccessfulCount(), "# tests succeeded");
assertEquals(0, eventRecorder.getTestFailedCount(), "# tests failed");
}
@Test
void executeTestsForPotentiallyIncompatibleTypeMethodInjectionCases() {
ExecutionEventRecorder eventRecorder = executeTestsForClass(
PotentiallyIncompatibleTypeMethodInjectionTestCase.class);
assertEquals(3, eventRecorder.getTestStartedCount(), "# tests started");
assertEquals(2, eventRecorder.getTestSuccessfulCount(), "# tests succeeded");
assertEquals(1, eventRecorder.getTestFailedCount(), "# tests failed");
// @formatter:off
Predicate<String> expectations = s ->
s.contains("NumberParameterResolver") &&
s.contains("resolved a value of type [java.lang.Integer]") &&
s.contains("but a value assignment compatible with [java.lang.Double] is required");
assertRecordedExecutionEventsContainsExactly(eventRecorder.getFailedTestFinishedEvents(),
event(
test("doubleParameterInjection"),
finishedWithFailure(allOf(isA(ParameterResolutionException.class), message(expectations)
))));
// @formatter:on
}
@Test
void executeTestsForMethodInjectionInBeforeAndAfterEachMethods() {
ExecutionEventRecorder eventRecorder = executeTestsForClass(BeforeAndAfterMethodInjectionTestCase.class);
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");
}
@Test
void executeTestsForMethodInjectionInBeforeAndAfterAllMethods() {
ExecutionEventRecorder eventRecorder = executeTestsForClass(BeforeAndAfterAllMethodInjectionTestCase.class);
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");
}
@Test
void executeTestsForMethodWithExtendWithAnnotation() {
ExecutionEventRecorder eventRecorder = executeTestsForClass(ExtendWithOnMethodTestCase.class);
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");
}
// -------------------------------------------------------------------
@ExtendWith(CustomTypeParameterResolver.class)
private static class ConstructorInjectionTestCase {
private final TestInfo outerTestInfo;
private final CustomType outerCustomType;
@SuppressWarnings("unused")
ConstructorInjectionTestCase(TestInfo testInfo, CustomType customType) {
this.outerTestInfo = testInfo;
this.outerCustomType = customType;
}
@Test
void test() {
assertNotNull(this.outerTestInfo);
assertNotNull(this.outerCustomType);
}
@Nested
class NestedTestCase {
private final TestInfo innerTestInfo;
private final CustomType innerCustomType;
@SuppressWarnings("unused")
NestedTestCase(TestInfo testInfo, CustomType customType) {
this.innerTestInfo = testInfo;
this.innerCustomType = customType;
}
@Test
void test() {
assertNotNull(outerTestInfo);
assertNotNull(outerCustomType);
assertNotNull(this.innerTestInfo);
assertNotNull(this.innerCustomType);
}
}
}
@ExtendWith({ CustomTypeParameterResolver.class, CustomAnnotationParameterResolver.class })
private static class MethodInjectionTestCase {
@Test
void parameterInjectionOfTestInfo(TestInfo testInfo) {
assertNotNull(testInfo);
}
@Test
void parameterInjectionWithCompetingResolversFail(@CustomAnnotation CustomType customType) {
// should fail
}
@Test
void parameterInjectionByType(CustomType customType) {
assertNotNull(customType);
}
@Test
void parameterInjectionByAnnotation(@CustomAnnotation String value) {
assertNotNull(value);
}
// some overloaded methods
@Test
void overloadedName() {
assertTrue(true);
}
@Test
void overloadedName(CustomType customType) {
assertNotNull(customType);
}
@Test
void overloadedName(CustomType customType, @CustomAnnotation String value) {
assertNotNull(customType);
assertNotNull(value);
}
}
@ExtendWith(NullIntegerParameterResolver.class)
private static class NullMethodInjectionTestCase {
@Test
void injectWrapper(Integer number) {
assertNull(number);
}
@Test
void injectPrimitive(int number) {
// should never be invoked since an int cannot be null
}
}
@ExtendWith(PrimitiveIntegerParameterResolver.class)
private static class PrimitiveIntegerMethodInjectionTestCase {
@Test
void intPrimitive(int i) {
assertEquals(42, i);
}
}
@ExtendWith(PrimitiveArrayParameterResolver.class)
private static class PrimitiveArrayMethodInjectionTestCase {
@Test
void primitiveArray(int... ints) {
assertArrayEquals(new int[] { 1, 2, 3 }, ints);
}
}
@ExtendWith(NumberParameterResolver.class)
private static class PotentiallyIncompatibleTypeMethodInjectionTestCase {
@Test
void numberParameterInjection(Number number) {
assertEquals(Integer.valueOf(42), number);
}
@Test
void integerParameterInjection(Integer number) {
assertEquals(Integer.valueOf(42), number);
}
/**
* This test must fail, since {@link Double} is a {@link Number} but not an {@link Integer}.
* @see NumberParameterResolver
*/
@Test
void doubleParameterInjection(Double number) {
/* no-op */
}
}
private static class BeforeAndAfterMethodInjectionTestCase {
@BeforeEach
void before(TestInfo testInfo) {
assertEquals("custom name", testInfo.getDisplayName());
}
@Test
@DisplayName("custom name")
void customNamedTest() {
}
@AfterEach
void after(TestInfo testInfo) {
assertEquals("custom name", testInfo.getDisplayName());
}
}
@DisplayName("custom class name")
private static class BeforeAndAfterAllMethodInjectionTestCase {
@BeforeAll
static void beforeAll(TestInfo testInfo) {
assertEquals("custom class name", testInfo.getDisplayName());
}
@Test
void aTest() {
}
@AfterAll
static void afterAll(TestInfo testInfo) {
assertEquals("custom class name", testInfo.getDisplayName());
}
}
private static class ExtendWithOnMethodTestCase {
/**
* This set-up / tear-down method is here to verify that {@code @BeforeEach}
* and {@code @AfterEach} methods are properly invoked using the same
* {@code ExtensionRegistry} as the one used for the corresponding
* {@code @Test} method.
*
* @see <a href="https://github.com/junit-team/junit5/issues/523">#523</a>
*/
@BeforeEach
@AfterEach
void setUpAndTearDown(CustomType customType, @CustomAnnotation String value) {
assertNotNull(customType);
assertNotNull(value);
}
@Test
@ExtendWith(CustomTypeParameterResolver.class)
@ExtendWith(CustomAnnotationParameterResolver.class)
void testMethodWithExtensionAnnotation(CustomType customType, @CustomAnnotation String value) {
assertNotNull(customType);
assertNotNull(value);
}
}
}