blob: a02039e1a46a31375d8aed8bc3d738318def6c94 [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.params;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.params.ParameterizedTestExtension.arguments;
import java.io.FileNotFoundException;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance.Lifecycle;
import org.junit.jupiter.api.extension.ExecutableInvoker;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.TestInstances;
import org.junit.jupiter.api.parallel.ExecutionMode;
import org.junit.jupiter.engine.execution.ExtensionValuesStore;
import org.junit.jupiter.engine.execution.NamespaceAwareStore;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
import org.junit.jupiter.params.provider.ArgumentsSource;
import org.junit.platform.commons.JUnitException;
import org.junit.platform.commons.PreconditionViolationException;
/**
* Unit tests for {@link ParameterizedTestExtension}.
*
* @since 5.0
*/
class ParameterizedTestExtensionTests {
private final ParameterizedTestExtension parameterizedTestExtension = new ParameterizedTestExtension();
static boolean streamWasClosed = false;
@Test
void supportsReturnsFalseForMissingTestMethod() {
var extensionContextWithoutTestMethod = getExtensionContextReturningSingleMethod(new TestCaseWithoutMethod());
assertFalse(this.parameterizedTestExtension.supportsTestTemplate(extensionContextWithoutTestMethod));
}
@Test
void supportsReturnsFalseForTestMethodWithoutParameterizedTestAnnotation() {
var extensionContextWithUnAnnotatedTestMethod = getExtensionContextReturningSingleMethod(
new TestCaseWithMethod());
assertFalse(this.parameterizedTestExtension.supportsTestTemplate(extensionContextWithUnAnnotatedTestMethod));
}
@Test
void supportsReturnsTrueForTestMethodWithParameterizedTestAnnotation() {
var extensionContextWithAnnotatedTestMethod = getExtensionContextReturningSingleMethod(
new TestCaseWithAnnotatedMethod());
assertTrue(this.parameterizedTestExtension.supportsTestTemplate(extensionContextWithAnnotatedTestMethod));
}
@Test
void streamsReturnedByProvidersAreClosedWhenCallingProvide() {
var extensionContext = getExtensionContextReturningSingleMethod(
new ArgumentsProviderWithCloseHandlerTestCase());
// we need to call supportsTestTemplate() first, because it creates and
// puts the ParameterizedTestMethodContext into the Store
this.parameterizedTestExtension.supportsTestTemplate(extensionContext);
var stream = this.parameterizedTestExtension.provideTestTemplateInvocationContexts(extensionContext);
assertFalse(streamWasClosed);
// cause the stream to be evaluated
stream.count();
assertTrue(streamWasClosed);
}
@Test
void emptyDisplayNameIsIllegal() {
var extensionContext = getExtensionContextReturningSingleMethod(new EmptyDisplayNameProviderTestCase());
assertThrows(PreconditionViolationException.class,
() -> this.parameterizedTestExtension.provideTestTemplateInvocationContexts(extensionContext));
}
@Test
void defaultDisplayNameWithEmptyStringInConfigurationIsIllegal() {
AtomicInteger invocations = new AtomicInteger();
Function<String, Optional<String>> configurationSupplier = key -> {
if (key.equals(ParameterizedTestExtension.DISPLAY_NAME_PATTERN_KEY)) {
invocations.incrementAndGet();
return Optional.of("");
}
else {
return Optional.empty();
}
};
var extensionContext = getExtensionContextReturningSingleMethod(new DefaultDisplayNameProviderTestCase(),
configurationSupplier);
assertThrows(PreconditionViolationException.class,
() -> this.parameterizedTestExtension.provideTestTemplateInvocationContexts(extensionContext));
assertEquals(1, invocations.get());
}
@Test
void argumentsRethrowsOriginalExceptionFromProviderAsUncheckedException() {
ArgumentsProvider failingProvider = (context) -> {
throw new FileNotFoundException("a message");
};
var exception = assertThrows(FileNotFoundException.class, () -> arguments(failingProvider, null));
assertEquals("a message", exception.getMessage());
}
@Test
void throwsExceptionWhenParameterizedTestIsNotInvokedAtLeastOnce() {
var extensionContextWithAnnotatedTestMethod = getExtensionContextReturningSingleMethod(
new TestCaseWithAnnotatedMethod());
var stream = this.parameterizedTestExtension.provideTestTemplateInvocationContexts(
extensionContextWithAnnotatedTestMethod);
// cause the stream to be evaluated
stream.toArray();
var exception = assertThrows(JUnitException.class, stream::close);
assertThat(exception).hasMessage(
"Configuration error: You must configure at least one set of arguments for this @ParameterizedTest");
}
@Test
void throwsExceptionWhenArgumentsProviderIsNotStatic() {
var extensionContextWithAnnotatedTestMethod = getExtensionContextReturningSingleMethod(
new NonStaticArgumentsProviderTestCase());
var stream = this.parameterizedTestExtension.provideTestTemplateInvocationContexts(
extensionContextWithAnnotatedTestMethod);
var exception = assertThrows(JUnitException.class, stream::toArray);
assertArgumentsProviderInstantiationException(exception, NonStaticArgumentsProvider.class);
}
@Test
void throwsExceptionWhenArgumentsProviderDoesNotContainNoArgumentConstructor() {
var extensionContextWithAnnotatedTestMethod = getExtensionContextReturningSingleMethod(
new MissingNoArgumentsConstructorArgumentsProviderTestCase());
var stream = this.parameterizedTestExtension.provideTestTemplateInvocationContexts(
extensionContextWithAnnotatedTestMethod);
var exception = assertThrows(JUnitException.class, stream::toArray);
assertArgumentsProviderInstantiationException(exception, MissingNoArgumentsConstructorArgumentsProvider.class);
}
private <T> void assertArgumentsProviderInstantiationException(JUnitException exception, Class<T> clazz) {
assertThat(exception).hasMessage(
String.format("Failed to find a no-argument constructor for ArgumentsProvider [%s]. "
+ "Please ensure that a no-argument constructor exists and "
+ "that the class is either a top-level class or a static nested class",
clazz.getName()));
}
private ExtensionContext getExtensionContextReturningSingleMethod(Object testCase) {
return getExtensionContextReturningSingleMethod(testCase, ignored -> Optional.empty());
}
private ExtensionContext getExtensionContextReturningSingleMethod(Object testCase,
Function<String, Optional<String>> configurationSupplier) {
// @formatter:off
var optional = Arrays.stream(testCase.getClass().getDeclaredMethods())
.filter(method -> method.getName().equals("method"))
.findFirst();
// @formatter:on
return new ExtensionContext() {
private final ExtensionValuesStore store = new ExtensionValuesStore(null);
@Override
public Optional<Method> getTestMethod() {
return optional;
}
@Override
public Optional<ExtensionContext> getParent() {
return Optional.empty();
}
@Override
public ExtensionContext getRoot() {
return this;
}
@Override
public String getUniqueId() {
return null;
}
@Override
public String getDisplayName() {
return null;
}
@Override
public Set<String> getTags() {
return null;
}
@Override
public Optional<AnnotatedElement> getElement() {
return Optional.empty();
}
@Override
public Optional<Class<?>> getTestClass() {
return Optional.empty();
}
@Override
public Optional<Lifecycle> getTestInstanceLifecycle() {
return Optional.empty();
}
@Override
public java.util.Optional<Object> getTestInstance() {
return Optional.empty();
}
@Override
public Optional<TestInstances> getTestInstances() {
return Optional.empty();
}
@Override
public Optional<Throwable> getExecutionException() {
return Optional.empty();
}
@Override
public Optional<String> getConfigurationParameter(String key) {
return configurationSupplier.apply(key);
}
@Override
public <T> Optional<T> getConfigurationParameter(String key, Function<String, T> transformer) {
return configurationSupplier.apply(key).map(transformer);
}
@Override
public void publishReportEntry(Map<String, String> map) {
}
@Override
public Store getStore(Namespace namespace) {
return new NamespaceAwareStore(store, namespace);
}
@Override
public ExecutionMode getExecutionMode() {
return ExecutionMode.SAME_THREAD;
}
@Override
public ExecutableInvoker getExecutableInvoker() {
return null;
}
};
}
static class TestCaseWithoutMethod {
}
static class TestCaseWithMethod {
void method() {
}
}
static class TestCaseWithAnnotatedMethod {
@ParameterizedTest
void method() {
}
}
static class ArgumentsProviderWithCloseHandlerTestCase {
@ParameterizedTest
@ArgumentsSource(ArgumentsProviderWithCloseHandler.class)
void method(String parameter) {
}
}
static class ArgumentsProviderWithCloseHandler implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
var argumentsStream = Stream.of("foo", "bar").map(Arguments::of);
return argumentsStream.onClose(() -> streamWasClosed = true);
}
}
static class NonStaticArgumentsProviderTestCase {
@ParameterizedTest
@ArgumentsSource(NonStaticArgumentsProvider.class)
void method() {
}
}
class NonStaticArgumentsProvider implements ArgumentsProvider {
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return null;
}
}
static class MissingNoArgumentsConstructorArgumentsProviderTestCase {
@ParameterizedTest
@ArgumentsSource(MissingNoArgumentsConstructorArgumentsProvider.class)
void method() {
}
}
static class EmptyDisplayNameProviderTestCase {
@ParameterizedTest(name = "")
@ArgumentsSource(MissingNoArgumentsConstructorArgumentsProvider.class)
void method() {
}
}
static class DefaultDisplayNameProviderTestCase {
@ParameterizedTest
@ArgumentsSource(MissingNoArgumentsConstructorArgumentsProvider.class)
void method() {
}
}
static class MissingNoArgumentsConstructorArgumentsProvider implements ArgumentsProvider {
MissingNoArgumentsConstructorArgumentsProvider(String parameter) {
}
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return null;
}
}
}