Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make parameterized ArgumentSource annotations repeatable #3787

Merged
merged 20 commits into from May 14, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -45,8 +45,9 @@ JUnit repository on GitHub.
[[release-notes-5.11.0-M2-junit-jupiter-new-features-and-improvements]]
==== New Features and Improvements

* ❓

* Support `@..Source` annotations as repeatable for parameterized tests. See the
<<../user-guide/index.adoc#writing-tests-parameterized-repeatable-sources, User Guide>>
for more details.

[[release-notes-5.11.0-M2-junit-vintage]]
=== JUnit Vintage
Expand Down
28 changes: 28 additions & 0 deletions documentation/src/docs/asciidoc/user-guide/writing-tests.adoc
Expand Up @@ -1963,6 +1963,34 @@ If you wish to implement a custom `ArgumentsProvider` that also consumes an anno
(like built-in providers such as `{ValueArgumentsProvider}` or `{CsvArgumentsProvider}`),
you have the possibility to extend the `{AnnotationBasedArgumentsProvider}` class.

[[writing-tests-parameterized-repeatable-sources]]
===== Multiple sources using repeatable annotations
Repeatable annotations provide a convenient way to specify multiple sources from
different providers.

[source,java,indent=0]
----
include::{testDir}/example/ParameterizedTestDemo.java[tags=repeatable_annotations]
----

Following the above parameterized test, a test case will run for each argument:

----
[1] foo
[2] bar
----

The following annotations are repeatable:

* `@ValueSource`
* `@EnumSource`
* `@MethodSource`
* `@FieldSource`
* `@CsvSource`
* `@CsvFileSource`
* `@ArgumentsSource`


[[writing-tests-parameterized-tests-argument-conversion]]
==== Argument Conversion

Expand Down
18 changes: 18 additions & 0 deletions documentation/src/test/java/example/ParameterizedTestDemo.java
Expand Up @@ -542,4 +542,22 @@ static Stream<Arguments> namedArguments() {
}
// end::named_arguments[]
// @formatter:on

// tag::repeatable_annotations[]
@DisplayName("A parameterized test that makes use of repeatable annotations")
@ParameterizedTest
@MethodSource("someProvider")
@MethodSource("otherProvider")
void testWithRepeatedAnnotation(String argument) {
assertNotNull(argument);
}

static Stream<String> someProvider() {
return Stream.of("foo");
}

static Stream<String> otherProvider() {
return Stream.of("bar");
}
// end::repeatable_annotations[]
}
Expand Up @@ -13,6 +13,8 @@
import static org.apiguardian.api.API.Status.EXPERIMENTAL;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import org.apiguardian.api.API;
Expand All @@ -39,17 +41,17 @@ public abstract class AnnotationBasedArgumentsProvider<A extends Annotation>
public AnnotationBasedArgumentsProvider() {
}

private A annotation;
private final List<A> annotations = new ArrayList<>();

@Override
public final void accept(A annotation) {
Preconditions.notNull(annotation, "annotation must not be null");
this.annotation = annotation;
annotations.add(annotation);
}

@Override
public final Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return provideArguments(context, this.annotation);
return annotations.stream().flatMap(annotation -> provideArguments(context, annotation));
}

/**
Expand Down
marcphilipp marked this conversation as resolved.
Show resolved Hide resolved
Expand Up @@ -14,6 +14,7 @@

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
Expand Down Expand Up @@ -63,6 +64,7 @@
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(CsvFileSources.class)
@API(status = STABLE, since = "5.7")
@ArgumentsSource(CsvFileArgumentsProvider.class)
@SuppressWarnings("exports")
Expand Down
@@ -0,0 +1,46 @@
/*
* Copyright 2015-2024 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.provider;

import static org.apiguardian.api.API.Status.STABLE;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.apiguardian.api.API;

/**
* {@code @CsvFileSources} is a simple container for one or more
* {@link CsvFileSource} annotations.
*
* <p>Note, however, that use of the {@code @CsvFileSources} container is completely
* optional since {@code @CsvFileSource} is a {@linkplain java.lang.annotation.Repeatable
* repeatable} annotation.
*
* @since 5.11
* @see CsvFileSource
* @see java.lang.annotation.Repeatable
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = STABLE, since = "5.11")
public @interface CsvFileSources {

/**
* An array of one or more {@link CsvFileSource @CsvFileSource}
* annotations.
*/
CsvFileSource[] value();
}
Expand Up @@ -14,6 +14,7 @@

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
Expand Down Expand Up @@ -64,6 +65,7 @@
*/
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(CsvSources.class)
@Documented
@API(status = STABLE, since = "5.7")
@ArgumentsSource(CsvArgumentsProvider.class)
Expand Down
@@ -0,0 +1,46 @@
/*
* Copyright 2015-2024 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.provider;

import static org.apiguardian.api.API.Status.STABLE;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.apiguardian.api.API;

/**
* {@code @CsvSources} is a simple container for one or more
* {@link CsvSource} annotations.
*
* <p>Note, however, that use of the {@code @CsvSources} container is completely
* optional since {@code @CsvSource} is a {@linkplain java.lang.annotation.Repeatable
* repeatable} annotation.
*
* @since 5.11
* @see CsvSource
* @see java.lang.annotation.Repeatable
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = STABLE, since = "5.11")
public @interface CsvSources {

/**
* An array of one or more {@link CsvSource @CsvSource}
* annotations.
*/
CsvSource[] value();
}
Expand Up @@ -16,6 +16,7 @@

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
Expand Down Expand Up @@ -49,6 +50,7 @@
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(EnumSources.class)
@API(status = STABLE, since = "5.7")
@ArgumentsSource(EnumArgumentsProvider.class)
@SuppressWarnings("exports")
Expand Down
@@ -0,0 +1,46 @@
/*
* Copyright 2015-2024 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.provider;

import static org.apiguardian.api.API.Status.STABLE;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.apiguardian.api.API;

/**
* {@code @EnumSources} is a simple container for one or more
* {@link EnumSource} annotations.
*
* <p>Note, however, that use of the {@code @EnumSources} container is completely
* optional since {@code @EnumSource} is a {@linkplain java.lang.annotation.Repeatable
* repeatable} annotation.
*
* @since 5.11
* @see EnumSource
* @see java.lang.annotation.Repeatable
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = STABLE, since = "5.11")
public @interface EnumSources {

/**
* An array of one or more {@link EnumSource @EnumSource}
* annotations.
*/
EnumSource[] value();
}
Expand Up @@ -14,6 +14,7 @@

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
Expand Down Expand Up @@ -112,6 +113,7 @@
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(FieldSources.class)
@API(status = EXPERIMENTAL, since = "5.11")
@ArgumentsSource(FieldArgumentsProvider.class)
@SuppressWarnings("exports")
Expand Down
@@ -0,0 +1,46 @@
/*
* Copyright 2015-2024 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.provider;

import static org.apiguardian.api.API.Status.STABLE;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.apiguardian.api.API;

/**
* {@code @FieldSources} is a simple container for one or more
* {@link FieldSource} annotations.
*
* <p>Note, however, that use of the {@code @FieldSources} container is completely
* optional since {@code @FieldSource} is a {@linkplain java.lang.annotation.Repeatable
* repeatable} annotation.
*
* @since 5.11
* @see FieldSource
* @see java.lang.annotation.Repeatable
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@API(status = STABLE, since = "5.11")
marcphilipp marked this conversation as resolved.
Show resolved Hide resolved
public @interface FieldSources {

/**
* An array of one or more {@link FieldSource @FieldSource}
* annotations.
*/
FieldSource[] value();
}
Expand Up @@ -14,6 +14,7 @@

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
Expand Down Expand Up @@ -103,6 +104,7 @@
@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(MethodSources.class)
@API(status = STABLE, since = "5.7")
@ArgumentsSource(MethodArgumentsProvider.class)
@SuppressWarnings("exports")
Expand Down