From 144793c3ef20143abe7e62b41e1a4abcf6e8611f Mon Sep 17 00:00:00 2001 From: Danil Pavlov Date: Mon, 13 Jul 2026 15:33:48 +0200 Subject: [PATCH 1/7] Add Kotlin code examples to last writing tests topics Signed-off-by: Danil Pavlov --- .../parameterized-classes-and-tests.adoc | 957 +++++++++++++++++- .../writing-tests/tagging-and-filtering.adoc | 16 + .../writing-tests/test-execution-order.adoc | 32 + .../example/kotlin/ExternalFieldSourceDemo.kt | 24 + .../kotlin/ExternalMethodSourceDemo.kt | 24 + .../MethodSourceParameterResolutionDemo.kt | 57 ++ .../kotlin/OrderedNestedTestClassesDemo.kt | 38 + .../kotlin/example/kotlin/OrderedTestsDemo.kt | 39 + .../example/kotlin/ParameterizedClassDemo.kt | 84 +- .../kotlin/ParameterizedLifecycleDemo.kt | 93 ++ .../example/kotlin/ParameterizedRecordDemo.kt | 48 + .../example/kotlin/ParameterizedTestDemo.kt | 630 ++++++++++++ .../test/kotlin/example/kotlin/TaggingDemo.kt | 25 + 13 files changed, 2046 insertions(+), 21 deletions(-) create mode 100644 documentation/src/test/kotlin/example/kotlin/ExternalFieldSourceDemo.kt create mode 100644 documentation/src/test/kotlin/example/kotlin/ExternalMethodSourceDemo.kt create mode 100644 documentation/src/test/kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt create mode 100644 documentation/src/test/kotlin/example/kotlin/OrderedNestedTestClassesDemo.kt create mode 100644 documentation/src/test/kotlin/example/kotlin/OrderedTestsDemo.kt create mode 100644 documentation/src/test/kotlin/example/kotlin/ParameterizedLifecycleDemo.kt create mode 100644 documentation/src/test/kotlin/example/kotlin/ParameterizedRecordDemo.kt create mode 100644 documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt create mode 100644 documentation/src/test/kotlin/example/kotlin/TaggingDemo.kt diff --git a/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc b/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc index 10b0377933b8..56f34448316c 100644 --- a/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc +++ b/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc @@ -22,10 +22,26 @@ parameterized method or class, respectively. The following example demonstrates a parameterized test that uses the `@ValueSource` annotation to specify a `String` array as the source of arguments. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=first_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=first_example] +---- +-- +==== When executing the above parameterized test method, each invocation will be reported separately. For instance, the `ConsoleLauncher` will print output similar to the @@ -41,10 +57,26 @@ palindromes(String) ✔ The same `@ValueSource` annotation can be used to specify the source of arguments for a `@ParameterizedClass`. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedClassDemo.java[tags=first_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedClassDemo.kt[tags=first_example] +---- +-- +==== When executing the above parameterized test class, each invocation will be reported separately. For instance, the `ConsoleLauncher` will print output similar to the @@ -66,7 +98,7 @@ PalindromeTests ✔ [[setup]] == Required Setup -In order to use parameterized classes or tests you need to add a dependency on the +In order to use parameterized classes or tests, you need to add a dependency on the `junit-jupiter-params` artifact. Please refer to xref:appendix.adoc#dependency-metadata[Dependency Metadata] for details. [[consuming-arguments]] @@ -116,18 +148,50 @@ For constructor injection, the same rules apply as defined for above. In the following example, two arguments are injected into the constructor of the test class. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedClassDemo.java[tags=constructor_injection] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedClassDemo.kt[tags=constructor_injection] +---- +-- +==== You may use _records_ to implement parameterized classes that avoid the boilerplate code of declaring a test class constructor. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedRecordDemo.java[tags=example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedRecordDemo.kt[tags=example] +---- +-- +==== [[consuming-arguments-field-injection]] ==== Field Injection @@ -151,10 +215,26 @@ with `@Parameter(index)`. An _aggregator_ is any `@Parameter`-annotated field of The following example demonstrates how to use field injection to consume multiple arguments in a parameterized class. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedClassDemo.java[tags=field_injection] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedClassDemo.kt[tags=field_injection] +---- +-- +==== If field injection is used, no constructor parameters will be resolved with arguments from the source. Other xref:writing-tests/dependency-injection-for-constructors-and-methods.adoc[`ParameterResolver` extensions] @@ -165,7 +245,7 @@ may resolve constructor parameters as usual, though. `{BeforeParameterizedClassInvocation}` and `{AfterParameterizedClassInvocation}` can also be used to consume arguments if their `injectArguments` attribute is set to `true` (the -default). If so, their method signatures must follow the same rules apply as defined for +default). If so, their method signatures must follow the same rules as defined for <> and additionally use the same parameter types as the _indexed parameters_ of the parameterized test class. Please refer to the Javadoc of `{BeforeParameterizedClassInvocation}` and @@ -230,10 +310,26 @@ The following types of literal values are supported by `@ValueSource`. For example, the following `@ParameterizedTest` method will be invoked three times, with the values `1`, `2`, and `3` respectively. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=ValueSource_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=ValueSource_example] +---- +-- +==== [[sources-null-and-empty]] === Null and Empty Sources @@ -266,18 +362,50 @@ You can also combine `@NullSource`, `@EmptySource`, and `@ValueSource` to test a range of `null`, _empty_, and _blank_ input. The following example demonstrates how to achieve this for strings. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=NullAndEmptySource_example1] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=NullAndEmptySource_example1] +---- +-- +==== Making use of the composed `@NullAndEmptySource` annotation simplifies the above as follows. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=NullAndEmptySource_example2] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=NullAndEmptySource_example2] +---- +-- +==== NOTE: Both variants of the `nullEmptyAndBlankStrings(String)` parameterized test method result in six invocations: 1 for `null`, 1 for the empty string, and 4 for the explicit @@ -288,10 +416,26 @@ blank strings supplied via `@ValueSource`. `@EnumSource` provides a convenient way to use `Enum` constants. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=EnumSource_example] +---- +-- +==== The annotation's `value` attribute is optional. When omitted, the declared type of the first parameter is used. The test will fail if it does not reference an enum type. @@ -300,18 +444,50 @@ is declared as `TemporalUnit`, i.e. the interface implemented by `ChronoUnit`, w an enum type. Changing the method parameter type to `ChronoUnit` allows you to omit the explicit enum type from the annotation as follows. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_example_autodetection] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=EnumSource_example_autodetection] +---- +-- +==== The annotation provides an optional `names` attribute that lets you specify which constants shall be used, like in the following example. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_include_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=EnumSource_include_example] +---- +-- +==== In addition to `names`, you can use the `from` and `to` attributes to specify a range of constants. The range starts from the constant specified in the `from` attribute and @@ -323,33 +499,97 @@ in the enum type, respectively. If all `names`, `from`, and `to` attributes are all constants will be used. The following example demonstrates how to specify a range of constants. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_range_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=EnumSource_range_example] +---- +-- +==== The `@EnumSource` annotation also provides an optional `mode` attribute that enables fine-grained control over which constants are passed to the test method. For example, you can exclude names from the enum constant pool or specify regular expressions as in the following examples. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_exclude_example] ---- +-- +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=EnumSource_exclude_example] +---- +-- +==== + +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_regex_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=EnumSource_regex_example] +---- +-- +==== You can also combine `mode` with the `from`, `to` and `names` attributes to define a range of constants while excluding specific values from that range as shown below. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=EnumSource_range_exclude_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=EnumSource_range_exclude_example] +---- +-- +==== [[sources-MethodSource]] === @MethodSource @@ -363,7 +603,7 @@ must always be `static`. Each factory method must generate a _stream_ of _arguments_, and each set of arguments within the stream will be provided as the physical arguments for individual invocations of -the annotated `@ParameterizedClass` or `@ParameterizedTest`. Generally speaking this +the annotated `@ParameterizedClass` or `@ParameterizedTest`. Generally speaking, this translates to a `Stream` of `Arguments` (i.e., `Stream`); however, the actual concrete return type can take on many forms. In this context, a "stream" is anything that JUnit can reliably convert into a `Stream`, such as `Stream`, `DoubleStream`, @@ -380,28 +620,83 @@ making it safe to use a resource such as `Files.lines()`. If you only need a single parameter, you can return a `Stream` of instances of the parameter type as demonstrated in the following example. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=simple_MethodSource_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=simple_MethodSource_example] + +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=stringProvider] +---- +-- +==== For a `@ParameterizedClass`, providing a factory method name via `@MethodSource` is mandatory. For a `@ParameterizedTest`, if you do not explicitly provide a factory method name, JUnit Jupiter will search for a _factory_ method with the same name as the current `@ParameterizedTest` method by convention. This is demonstrated in the following example. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=simple_MethodSource_without_value_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=simple_MethodSource_without_value_example] +---- + +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=testWithDefaultLocalMethodSource_provider] +---- +-- +==== Streams for primitive types (`DoubleStream`, `IntStream`, and `LongStream`) are also supported as demonstrated by the following example. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=primitive_MethodSource_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[lines=189..193;567] +---- +-- +==== If a parameterized class or test method declares multiple parameters, you need to return a collection, stream, or array of `Arguments` instances or object arrays as shown below (see @@ -410,20 +705,52 @@ the Javadoc for `{MethodSource}` for further details on supported return types). addition, `Arguments.of(Object...)` may be used as an alternative to `arguments(Object...)`. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=multi_arg_MethodSource_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=multi_arg_MethodSource_example;stringIntAndListProvider] +---- +-- +==== An external, `static` _factory_ method can be referenced by providing its _fully qualified method name_ as demonstrated in the following example. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- package example; include::example$java/example/ExternalMethodSourceDemo.java[tags=external_MethodSource_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ExternalMethodSourceDemo.kt[tags=external_MethodSource_example] +---- +-- +==== Factory methods can declare parameters, which will be provided by registered implementations of the `ParameterResolver` extension API. In the following example, the @@ -434,10 +761,26 @@ provided to differentiate them – for example, `@MethodSource("factoryMethod()" can be referenced by its fully qualified method name — for example, `@MethodSource("example.MyTests#factoryMethod(java.lang.String)")`. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/MethodSourceParameterResolutionDemo.java[tags=parameter_resolution_MethodSource_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt[tags=parameter_resolution_MethodSource_example] +---- +-- +==== [[sources-FieldSource]] === @FieldSource @@ -454,7 +797,7 @@ within the "stream" will be provided as the physical arguments for individual in of the annotated `@ParameterizedClass` or `@ParameterizedTest`. In this context, a "stream" is anything that JUnit can reliably convert to a `Stream`; -however, the actual concrete field type can take on many forms. Generally speaking this +however, the actual concrete field type can take on many forms. Generally speaking, this translates to a `Collection`, an `Iterable`, a `Supplier` of a stream (`Stream`, `DoubleStream`, `LongStream`, or `IntStream`), a `Supplier` of an `Iterator`, an array of objects or primitives, or any type that provides an `iterator(): Iterator` method (such @@ -490,19 +833,61 @@ search in the test class for a field that has the same name as the current This parameterized test method will be invoked twice: with the values `"apple"` and `"banana"`. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=default_field_FieldSource_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=default_field_FieldSource_example] +---- + +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=arrayOfFruits] +---- +-- +==== The following example demonstrates how to provide a single explicit field name via `@FieldSource`. This parameterized test method will be invoked twice: with the values `"apple"` and `"banana"`. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=explicit_field_FieldSource_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=explicit_field_FieldSource_example] +---- + +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=listOfFruits] +---- +-- +==== The following example demonstrates how to provide multiple explicit field names via `@FieldSource`. This example uses the `listOfFruits` field from the previous example as @@ -510,10 +895,36 @@ well as the `additionalFruits` field. Consequently, this parameterized test meth be invoked four times: with the values `"apple"`, `"banana"`, `"cherry"`, and `"dewberry"`. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=multiple_fields_FieldSource_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=multiple_fields_FieldSource_example] +---- + +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=listOfFruits] +---- + +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=additionalFruits] +---- +-- +==== It is also possible to provide a `Stream`, `DoubleStream`, `IntStream`, `LongStream`, or `Iterator` as the source of arguments via a `@FieldSource` field as long as the stream or @@ -522,10 +933,31 @@ how to provide a `Supplier` of a `Stream` of named arguments. This parameterized method will be invoked twice: with the values `"apple"` and `"banana"` and with display names `"Apple"` and `"Banana"`, respectively. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=named_arguments_FieldSource_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=named_arguments_FieldSource_example] +---- + +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=namedArgumentsSupplier] +---- +-- +==== [NOTE] ==== @@ -541,10 +973,31 @@ If a parameterized class or test method declares multiple parameters, the corres `Arguments` instances or object arrays as shown below (see the Javadoc for `{FieldSource}` for further details on supported types). +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=multi_arg_FieldSource_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=multi_arg_FieldSource_example] +---- + +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=stringIntAndListArguments] +---- +-- +==== [NOTE] ==== @@ -555,10 +1008,26 @@ Note that `arguments(Object...)` is a static factory method defined in the An external, `static` `@FieldSource` field can be referenced by providing its _fully qualified field name_ as demonstrated in the following example. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ExternalFieldSourceDemo.java[tags=external_field_FieldSource_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ExternalFieldSourceDemo.kt[tags=external_field_FieldSource_example] +---- +-- +==== [[sources-CsvSource]] === @CsvSource @@ -569,10 +1038,26 @@ represents a CSV record and results in one invocation of the parameterized class test. The first record may optionally be used to supply CSV headers (see the Javadoc for the `useHeadersInDisplayName` attribute for details and an example). +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=CsvSource_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=CsvSource_example] +---- +-- +==== The default delimiter is a comma (`,`), but you can use another character by setting the `delimiter` attribute. Alternatively, the `delimiterString` attribute allows you to use a @@ -616,6 +1101,11 @@ example below. Using a text block, the previous example can be implemented as follows. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- @ParameterizedTest @@ -630,6 +1120,27 @@ void testWithCsvSource(String fruit, int rank) { // ... } ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +@ParameterizedTest +@CsvSource(useHeadersInDisplayName = true, textBlock = """ + FRUIT, RANK + apple, 1 + banana, 2 + 'lemon, lime', 0xF1 + strawberry, 700_000 + """) +fun testWithCsvSource(fruit: String, rank: Int) { + // ... +} +---- +-- +==== The generated display names for the previous example include the CSV header names. @@ -652,6 +1163,11 @@ be placed either at the end of the last line of input or on the following line, left aligned with the rest of the input (as can be seen in the example below which demonstrates formatting similar to a table). +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- @ParameterizedTest @@ -672,12 +1188,39 @@ void testWithCsvSource(String fruit, int rank) { // ... } ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +@ParameterizedTest +@CsvSource(delimiter = '|', quoteCharacter = '"', textBlock = """ + #----------------------------- + # FRUIT | RANK + #----------------------------- + apple | 1 + #----------------------------- + banana | 2 + #----------------------------- + "lemon lime" | 0xF1 + #----------------------------- + strawberry | 700_000 + #----------------------------- + """) +fun testWithCsvSource(fruit: String, rank: Int) { + // ... +} +---- +-- +==== [NOTE] ==== Java's https://docs.oracle.com/en/java/javase/17/text-blocks/index.html[text block] feature automatically removes _incidental whitespace_ when the code is compiled. -However other JVM languages such as Groovy and Kotlin do not. Thus, if you are using a +However, other JVM languages such as Groovy and Kotlin do not. Thus, if you are using a programming language other than Java and your text block contains comments or new lines within quoted strings, you will need to ensure that there is no leading whitespace within your text block. @@ -703,10 +1246,26 @@ cannot be set simultaneously. NOTE: Any line beginning with the value of the `commentCharacter` attribute (`+++#+++` by default) will be interpreted as a comment and will be ignored. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=CsvFileSource_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=CsvFileSource_example] +---- +-- +==== [source,csv,indent=0] .two-column.csv @@ -757,15 +1316,47 @@ by default. This behavior can be changed by setting the that an implementation of `ArgumentsProvider` must be declared as either a top-level class or as a `static` nested class. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsSource_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=ArgumentsSource_example] +---- +-- +==== +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsProvider_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=ArgumentsProvider_example] +---- +-- +==== If you wish to implement a custom `ArgumentsProvider` that also consumes an annotation (like built-in providers such as `{ValueArgumentsProvider}` or `{CsvArgumentsProvider}`), @@ -775,10 +1366,26 @@ Moreover, `ArgumentsProvider` implementations may declare constructor parameters they need to be resolved by a registered `ParameterResolver` as demonstrated in the following example. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsProviderWithConstructorInjection_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=ArgumentsProviderWithConstructorInjection_example] +---- +-- +==== [[repeatable-sources]] === Multiple sources using repeatable annotations @@ -786,10 +1393,36 @@ include::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsProviderW Repeatable annotations provide a convenient way to specify multiple sources from different providers. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=repeatable_annotations] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=repeatable_annotations] +---- + +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=someProvider] +---- + +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=otherProvider] +---- +-- +==== Following the above parameterized test, a test case will run for each argument: @@ -826,10 +1459,26 @@ To change this behavior for a single parameterized class or test method, use the `argumentCountValidation` attribute of the `@ParameterizedClass` or `@ParameterizedTest` annotation: +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=argument_count_validation] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=argument_count_validation] +---- +-- +==== [[argument-conversion]] == Argument Conversion @@ -855,10 +1504,26 @@ For example, if a `@ParameterizedClass` or `@ParameterizedTest` declares a param of type `TimeUnit` and the actual type supplied by the declared source is a `String`, the string will be automatically converted into the corresponding `TimeUnit` enum constant. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=implicit_conversion_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=implicit_conversion_example] +---- +-- +==== `String` instances are implicitly converted to the following target types. @@ -920,7 +1585,7 @@ or _factory constructor_ as defined below. accepts either a single `String` argument or a single `CharSequence` argument and returns an instance of the target type. The name of the method can be arbitrary and need not follow any particular convention. -- __factory constructor__: a non-private constructor in the target type that accepts a +- __factory constructor__: a non-private constructor in the target type that accepts either a single `String` argument or a single `CharSequence` argument. Note that the target type must be declared as either a top-level class or as a `static` nested class. @@ -940,15 +1605,47 @@ For example, in the following `@ParameterizedTest` method, the `Book` argument w created by invoking the `Book.fromTitle(String)` factory method and passing `"42 Cats"` as the title of the book. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=implicit_fallback_conversion_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=implicit_fallback_conversion_example] +---- +-- +==== +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=implicit_fallback_conversion_example_Book] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=implicit_fallback_conversion_example_Book] +---- +-- +==== [[argument-conversion-explicit]] === Explicit Conversion @@ -958,33 +1655,97 @@ Instead of relying on implicit argument conversion, you may explicitly specify a like in the following example. Note that an implementation of `ArgumentConverter` must be declared as either a top-level class or as a `static` nested class. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=explicit_conversion_example] ---- +-- +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=explicit_conversion_example] +---- +-- +==== + +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=explicit_conversion_example_ToStringArgumentConverter] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=explicit_conversion_example_ToStringArgumentConverter] +---- +-- +==== If the converter is only meant to convert one type to another, you can extend `TypedArgumentConverter` to avoid boilerplate type checks. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=explicit_conversion_example_TypedArgumentConverter] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=explicit_conversion_example_TypedArgumentConverter] +---- +-- +==== Explicit argument converters are meant to be implemented by test and extension authors. Thus, `junit-jupiter-params` only provides a single explicit argument converter that may also serve as a reference implementation: `JavaTimeArgumentConverter`. It is used via the composed annotation `JavaTimeConversionPattern`. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=explicit_java_time_converter] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=explicit_java_time_converter] +---- +-- +==== If you wish to implement a custom `{ArgumentConverter}` that also consumes an annotation (like `JavaTimeArgumentConverter`), you have the possibility to extend the @@ -1006,10 +1767,26 @@ test method. In addition, type conversion is supported as discussed in Besides, you can retrieve the current test invocation index with `ArgumentsAccessor.getInvocationIndex()`. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsAccessor_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=ArgumentsAccessor_example] +---- +-- +==== _An instance of `ArgumentsAccessor` is automatically injected into any parameter of type `ArgumentsAccessor`._ @@ -1028,15 +1805,47 @@ provided as an argument for the corresponding parameter when the parameterized t invoked. Note that an implementation of `ArgumentsAggregator` must be declared as either a top-level class or as a `static` nested class. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsAggregator_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=ArgumentsAggregator_example] +---- +-- +==== +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsAggregator_example_PersonAggregator] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=ArgumentsAggregator_example_PersonAggregator] +---- +-- +==== If you find yourself repeatedly declaring `@AggregateWith(MyTypeAggregator.class)` for multiple parameterized classes or methods across your codebase, you may wish to create a @@ -1044,15 +1853,47 @@ custom _composed annotation_ such as `@CsvToMyType` that is meta-annotated with `@AggregateWith(MyTypeAggregator.class)`. The following example demonstrates this in action with a custom `@CsvToPerson` annotation. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsAggregator_with_custom_annotation_example] ---- +-- +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=ArgumentsAggregator_with_custom_annotation_example] +---- +-- +==== + +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=ArgumentsAggregator_with_custom_annotation_example_CsvToPerson] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=ArgumentsAggregator_with_custom_annotation_example_CsvToPerson] +---- +-- +==== [[display-names]] @@ -1075,11 +1916,27 @@ for Kotlin. However, you can customize invocation display names via the `name` attribute of the `@ParameterizedClass` or `@ParameterizedTest` annotation as in the following example. -====== +======== +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=custom_display_names] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=custom_display_names] +---- +-- +==== When executing the above method using the `ConsoleLauncher` you will see output similar to the following. @@ -1090,7 +1947,7 @@ Display name of container ✔ ├─ 2 ==> the rank of "banana" is "2" ✔ └─ 3 ==> the rank of "lemon, lime" is "3" ✔ .... -====== +======== [NOTE] ==== @@ -1125,11 +1982,32 @@ Use the `{Named}` API to provide a custom name for an individual argument, and t name will be used if the argument is included in the invocation display name, like in the example below. -====== +======== +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=named_arguments] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=named_arguments] +---- + +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=namedArguments] +---- +-- +==== When executing the above method using the `ConsoleLauncher` you will see output similar to the following. @@ -1139,7 +2017,7 @@ A parameterized test with named arguments ✔ ├─ 1: An important file ✔ └─ 2: Another file ✔ .... -====== +======== [NOTE] ==== @@ -1153,11 +2031,32 @@ Similarly, `named(String, Object)` is a static factory method defined in the Use the `ArgumentSet` API to provide a custom name for the entire set of arguments, and the custom name will be used as the display name, like in the example below. -====== +======== +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=named_argument_set] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=named_argument_set] +---- + +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=argumentSets] +---- +-- +==== When executing the above method using the `ConsoleLauncher` you will see output similar to the following. @@ -1167,7 +2066,7 @@ A parameterized test with named argument sets ✔ ├─ [1] Important files ✔ └─ [2] Other files ✔ .... -====== +======== [NOTE] ==== @@ -1275,10 +2174,26 @@ parameter list. Since a test class may contain regular tests as well as paramete tests with different parameter lists, values from argument sources are not resolved for lifecycle methods (for example, `@BeforeEach`) and test class constructors. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/ParameterizedTestDemo.java[tags=ParameterResolver_example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=ParameterResolver_example] +---- +-- +==== [[lifecycle-interop-classes]] === Parameterized Classes @@ -1309,11 +2224,27 @@ arguments of the parameterized class (see the Javadoc of details). This may, for example, be used to initialize the used arguments as demonstrated by the following example. -[source,java,indent=0] .Using parameterized class lifecycle methods +[tabs] +==== +Java:: ++ +-- +[source,java,indent=0] ---- include::example$java/example/ParameterizedLifecycleDemo.java[tags=example] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/ParameterizedLifecycleDemo.kt[tags=example] +---- +-- +==== <1> Initialization of the argument _before_ each invocation of the parameterized class <2> Usage of the previously initialized argument in a test method <3> Validation and cleanup of the argument _after_ each invocation of the parameterized diff --git a/documentation/modules/ROOT/pages/writing-tests/tagging-and-filtering.adoc b/documentation/modules/ROOT/pages/writing-tests/tagging-and-filtering.adoc index ad5bb6c07300..5e7f6f2a07f5 100644 --- a/documentation/modules/ROOT/pages/writing-tests/tagging-and-filtering.adoc +++ b/documentation/modules/ROOT/pages/writing-tests/tagging-and-filtering.adoc @@ -5,10 +5,26 @@ used to filter xref:running-tests/intro.adoc[test discovery and execution]. Plea xref:running-tests/tags.adoc[] section for more information about tag support in the JUnit Platform. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/TaggingDemo.java[tags=user_guide] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/TaggingDemo.kt[tags=user_guide] +---- +-- +==== TIP: See xref:writing-tests/annotations.adoc#annotations[Meta-Annotations and Composed Annotations] for examples demonstrating how to create custom annotations for tags. diff --git a/documentation/modules/ROOT/pages/writing-tests/test-execution-order.adoc b/documentation/modules/ROOT/pages/writing-tests/test-execution-order.adoc index 20bae4e323fb..ef292c51e34e 100644 --- a/documentation/modules/ROOT/pages/writing-tests/test-execution-order.adoc +++ b/documentation/modules/ROOT/pages/writing-tests/test-execution-order.adoc @@ -41,10 +41,26 @@ NOTE: See also: xref:extensions/relative-execution-order-of-user-code-and-extens The following example demonstrates how to guarantee that test methods are executed in the order specified via the `@Order` annotation. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/OrderedTestsDemo.java[tags=user_guide] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/OrderedTestsDemo.kt[tags=user_guide] +---- +-- +==== [[methods-default]] === Setting the Default Method Orderer @@ -132,7 +148,23 @@ Note that a local `@TestClassOrder` declaration always overrides an inherited The following example demonstrates how to guarantee that `@Nested` test classes are executed in the order specified via the `@Order` annotation. +[tabs] +==== +Java:: ++ +-- [source,java,indent=0] ---- include::example$java/example/OrderedNestedTestClassesDemo.java[tags=user_guide] ---- +-- + +Kotlin:: ++ +-- +[source,kotlin,indent=0] +---- +include::example$kotlin/example/kotlin/OrderedNestedTestClassesDemo.kt[tags=user_guide] +---- +-- +==== diff --git a/documentation/src/test/kotlin/example/kotlin/ExternalFieldSourceDemo.kt b/documentation/src/test/kotlin/example/kotlin/ExternalFieldSourceDemo.kt new file mode 100644 index 000000000000..2b49207a85f4 --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/ExternalFieldSourceDemo.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2015-2026 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 example.kotlin + +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.FieldSource + +class ExternalFieldSourceDemo { + // tag::external_field_FieldSource_example[] + @ParameterizedTest + @FieldSource("example.FruitUtils#tropicalFruits") + fun testWithExternalFieldSource(tropicalFruit: String) { + // test with tropicalFruit + } + // end::external_field_FieldSource_example[] +} diff --git a/documentation/src/test/kotlin/example/kotlin/ExternalMethodSourceDemo.kt b/documentation/src/test/kotlin/example/kotlin/ExternalMethodSourceDemo.kt new file mode 100644 index 000000000000..cfd656e16c47 --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/ExternalMethodSourceDemo.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2015-2026 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 example.kotlin + +// tag::external_MethodSource_example[] +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.MethodSource + +class ExternalMethodSourceDemo { + @ParameterizedTest + @MethodSource("example.StringsProviders#tinyStrings") + fun testWithExternalMethodSource(tinyString: String) { + // test with tiny string + } +} +// end::external_MethodSource_example[] diff --git a/documentation/src/test/kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt b/documentation/src/test/kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt new file mode 100644 index 000000000000..01fd844e9e7b --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt @@ -0,0 +1,57 @@ +/* + * Copyright 2015-2026 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 example.kotlin + +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.extension.ParameterContext +import org.junit.jupiter.api.extension.ParameterResolver +import org.junit.jupiter.api.extension.RegisterExtension +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.Arguments.arguments +import org.junit.jupiter.params.provider.MethodSource +import java.util.stream.Stream + +class MethodSourceParameterResolutionDemo { + // tag::parameter_resolution_MethodSource_example[] + @ParameterizedTest + @MethodSource("factoryMethodWithArguments") + fun testWithFactoryMethodWithArguments(argument: String) { + assertTrue(argument.startsWith("2")) + } + + class IntegerResolver : ParameterResolver { + override fun supportsParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext + ): Boolean = parameterContext.parameter.type == Int::class.javaPrimitiveType + + override fun resolveParameter( + parameterContext: ParameterContext, + extensionContext: ExtensionContext + ): Any = 2 + } + // end::parameter_resolution_MethodSource_example[] + + companion object { + @JvmField + @RegisterExtension + val integerResolver = IntegerResolver() + + @JvmStatic + fun factoryMethodWithArguments(quantity: Int): Stream = + Stream.of( + arguments("$quantity apples"), + arguments("$quantity lemons") + ) + } +} diff --git a/documentation/src/test/kotlin/example/kotlin/OrderedNestedTestClassesDemo.kt b/documentation/src/test/kotlin/example/kotlin/OrderedNestedTestClassesDemo.kt new file mode 100644 index 000000000000..8552e8d6b87a --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/OrderedNestedTestClassesDemo.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2015-2026 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 example.kotlin + +// tag::user_guide[] +import org.junit.jupiter.api.ClassOrderer +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Order +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestClassOrder + +@TestClassOrder(ClassOrderer.OrderAnnotation::class) +class OrderedNestedTestClassesDemo { + @Nested + @Order(1) + inner class PrimaryTests { + @Test + fun test1() { + } + } + + @Nested + @Order(2) + inner class SecondaryTests { + @Test + fun test2() { + } + } +} +// end::user_guide[] diff --git a/documentation/src/test/kotlin/example/kotlin/OrderedTestsDemo.kt b/documentation/src/test/kotlin/example/kotlin/OrderedTestsDemo.kt new file mode 100644 index 000000000000..a0fa88d81268 --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/OrderedTestsDemo.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2015-2026 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 example.kotlin + +// tag::user_guide[] +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation +import org.junit.jupiter.api.Order +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestMethodOrder + +@TestMethodOrder(OrderAnnotation::class) +class OrderedTestsDemo { + @Test + @Order(1) + fun nullValues() { + // perform assertions against null values + } + + @Test + @Order(2) + fun emptyValues() { + // perform assertions against empty values + } + + @Test + @Order(3) + fun validValues() { + // perform assertions against valid values + } +} +// end::user_guide[] diff --git a/documentation/src/test/kotlin/example/kotlin/ParameterizedClassDemo.kt b/documentation/src/test/kotlin/example/kotlin/ParameterizedClassDemo.kt index d1d9580c3567..ef864c8a70a8 100644 --- a/documentation/src/test/kotlin/example/kotlin/ParameterizedClassDemo.kt +++ b/documentation/src/test/kotlin/example/kotlin/ParameterizedClassDemo.kt @@ -10,31 +10,99 @@ package example.kotlin +import example.util.StringUtils import org.junit.jupiter.api.Assertions.assertFalse import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test import org.junit.jupiter.api.parallel.Execution import org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD import org.junit.jupiter.params.Parameter import org.junit.jupiter.params.ParameterizedClass import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.provider.CsvSource import org.junit.jupiter.params.provider.ValueSource import java.time.Duration class ParameterizedClassDemo { - // @formatter:off + @Nested + // tag::first_example[] + @ParameterizedClass + @ValueSource(strings = ["racecar", "radar", "able was I ere I saw elba"]) + inner class PalindromeTests { + @Parameter + lateinit var candidate: String + + @Test + fun palindrome() { + assertTrue(StringUtils.isPalindrome(candidate)) + } + + @Test + fun reversePalindrome() { + val reverseCandidate = candidate.reversed() + assertTrue(StringUtils.isPalindrome(reverseCandidate)) + } + } + // end::first_example[] + + @Nested + inner class ConstructorInjection { + @Nested + // tag::constructor_injection[] + @ParameterizedClass + @CsvSource("apple, 23", "banana, 42") + inner class FruitTests( + private val fruit: String, + private val quantity: Int + ) { + @Test + fun test() { + assertFruit(fruit) + assertQuantity(quantity) + } + + @Test + fun anotherTest() { + // ... + } + } + // end::constructor_injection[] + } + + @Nested + inner class FieldInjection { + @Nested + // tag::field_injection[] + @ParameterizedClass + @CsvSource("apple, 23", "banana, 42") + inner class FruitTests { + @Parameter(0) + lateinit var fruit: String + + @Parameter(1) + var quantity: Int = 0 + + @Test + fun test() { + assertFruit(fruit) + assertQuantity(quantity) + } + + @Test + fun anotherTest() { + // ... + } + } + // end::field_injection[] + } + @Nested // tag::nested[] @Execution(SAME_THREAD) @ParameterizedClass @ValueSource(strings = ["apple", "banana"]) - // end::nested[] - inner - // tag::nested[] - class FruitTests { - // end::nested[] - // @formatter:on - // tag::nested[] + inner class FruitTests { @Parameter lateinit var fruit: String diff --git a/documentation/src/test/kotlin/example/kotlin/ParameterizedLifecycleDemo.kt b/documentation/src/test/kotlin/example/kotlin/ParameterizedLifecycleDemo.kt new file mode 100644 index 000000000000..b5f6f5f98dc5 --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/ParameterizedLifecycleDemo.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2026 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 + */ + +@file:JvmName("ParameterizedLifecycleDemo") + +/* + * Copyright 2015-2026 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 example.kotlin + +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir +import org.junit.jupiter.params.AfterParameterizedClassInvocation +import org.junit.jupiter.params.BeforeParameterizedClassInvocation +import org.junit.jupiter.params.Parameter +import org.junit.jupiter.params.ParameterizedClass +import org.junit.jupiter.params.provider.MethodSource +import java.nio.file.Files +import java.nio.file.Path + +// tag::example[] +class TextFile( + val fileName: String, + val content: String +) { + var path: Path? = null + + override fun toString(): String = fileName +} + +fun textFiles(): List = + listOf( + // tag::custom_line_break[] + TextFile("file1", "first content"), + // tag::custom_line_break[] + TextFile("file2", "second content") +// tag::custom_line_break[] + ) + +@ParameterizedClass +@MethodSource("textFiles") +class TextFileTests { + @Parameter + lateinit var textFile: TextFile + + companion object { + @JvmStatic + @BeforeParameterizedClassInvocation + fun beforeInvocation( + textFile: TextFile, + @TempDir tempDir: Path + ) { // <1> + val filePath = tempDir.resolve(textFile.fileName) + textFile.path = Files.writeString(filePath, textFile.content) + } + + @JvmStatic + @AfterParameterizedClassInvocation + fun afterInvocation(textFile: TextFile) { // <3> + val actualContent = Files.readString(textFile.path!!) + assertEquals(textFile.content, actualContent, "Content must not have changed") + // Custom cleanup logic, if necessary + // File will be deleted automatically by @TempDir support + } + } + + @Test + fun test() { + assertTrue(Files.exists(textFile.path!!)) // <2> + } + + @Test + fun anotherTest() { + // ... + } +} +// end::example[] diff --git a/documentation/src/test/kotlin/example/kotlin/ParameterizedRecordDemo.kt b/documentation/src/test/kotlin/example/kotlin/ParameterizedRecordDemo.kt new file mode 100644 index 000000000000..a86fce2ea2e8 --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/ParameterizedRecordDemo.kt @@ -0,0 +1,48 @@ +/* + * Copyright 2015-2026 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 example.kotlin + +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.params.ParameterizedClass +import org.junit.jupiter.params.provider.CsvSource + +class ParameterizedRecordDemo { + @Nested + // tag::example[] + @ParameterizedClass + @CsvSource("apple, 23", "banana, 42") + inner class FruitTests( + private val fruit: String, + private val quantity: Int + ) { + @Test + fun test() { + assertFruit(fruit) + assertQuantity(quantity) + } + + @Test + fun anotherTest() { + // ... + } + } + // end::example[] + + private fun assertFruit(fruit: String) { + assertTrue(listOf("apple", "banana", "cherry", "dewberry").contains(fruit)) + } + + private fun assertQuantity(quantity: Int) { + assertTrue(quantity >= 0) + } +} diff --git a/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt b/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt new file mode 100644 index 000000000000..e7753aff6958 --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt @@ -0,0 +1,630 @@ +/* + * Copyright 2015-2026 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 example.kotlin + +import example.domain.Person +import example.domain.Person.Gender +import example.util.StringUtils +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertFalse +import org.junit.jupiter.api.Assertions.assertNotEquals +import org.junit.jupiter.api.Assertions.assertNotNull +import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Named.named +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.TestInfo +import org.junit.jupiter.api.TestReporter +import org.junit.jupiter.api.extension.AnnotatedElementContext +import org.junit.jupiter.api.extension.ExtensionContext +import org.junit.jupiter.api.parallel.Execution +import org.junit.jupiter.api.parallel.ExecutionMode.SAME_THREAD +import org.junit.jupiter.params.ArgumentCountValidationMode +import org.junit.jupiter.params.ParameterizedTest +import org.junit.jupiter.params.aggregator.AggregateWith +import org.junit.jupiter.params.aggregator.ArgumentsAccessor +import org.junit.jupiter.params.aggregator.SimpleArgumentsAggregator +import org.junit.jupiter.params.converter.ConvertWith +import org.junit.jupiter.params.converter.JavaTimeConversionPattern +import org.junit.jupiter.params.converter.SimpleArgumentConverter +import org.junit.jupiter.params.converter.TypedArgumentConverter +import org.junit.jupiter.params.provider.Arguments +import org.junit.jupiter.params.provider.Arguments.arguments +import org.junit.jupiter.params.provider.ArgumentsProvider +import org.junit.jupiter.params.provider.ArgumentsSource +import org.junit.jupiter.params.provider.CsvFileSource +import org.junit.jupiter.params.provider.CsvSource +import org.junit.jupiter.params.provider.EmptySource +import org.junit.jupiter.params.provider.EnumSource +import org.junit.jupiter.params.provider.EnumSource.Mode.EXCLUDE +import org.junit.jupiter.params.provider.EnumSource.Mode.MATCH_ALL +import org.junit.jupiter.params.provider.FieldSource +import org.junit.jupiter.params.provider.MethodSource +import org.junit.jupiter.params.provider.NullAndEmptySource +import org.junit.jupiter.params.provider.NullSource +import org.junit.jupiter.params.provider.ValueSource +import org.junit.jupiter.params.support.ParameterDeclarations +import java.io.File +import java.time.LocalDate +import java.time.temporal.ChronoUnit +import java.time.temporal.TemporalUnit +import java.util.EnumSet +import java.util.function.Supplier +import java.util.stream.IntStream +import java.util.stream.Stream + +@Execution(SAME_THREAD) +class ParameterizedTestDemo { + @BeforeEach + fun printDisplayName(testInfo: TestInfo) { + println(testInfo.displayName) + } + + // tag::first_example[] + @ParameterizedTest + @ValueSource(strings = ["racecar", "radar", "able was I ere I saw elba"]) + fun palindromes(candidate: String) { + assertTrue(StringUtils.isPalindrome(candidate)) + } + // end::first_example[] + + // tag::ValueSource_example[] + @ParameterizedTest + @ValueSource(ints = [1, 2, 3]) + fun testWithValueSource(argument: Int) { + assertTrue(argument in 1..<4) + } + // end::ValueSource_example[] + + @Nested + inner class NullAndEmptySource1 { + // tag::NullAndEmptySource_example1[] + @ParameterizedTest + @NullSource + @EmptySource + @ValueSource(strings = [" ", " ", "\t", "\n"]) + fun nullEmptyAndBlankStrings(text: String?) { + assertTrue(text.isNullOrBlank()) + } + // end::NullAndEmptySource_example1[] + } + + @Nested + inner class NullAndEmptySource2 { + // tag::NullAndEmptySource_example2[] + @ParameterizedTest + @NullAndEmptySource + @ValueSource(strings = [" ", " ", "\t", "\n"]) + fun nullEmptyAndBlankStrings(text: String?) { + assertTrue(text.isNullOrBlank()) + } + // end::NullAndEmptySource_example2[] + } + + // tag::EnumSource_example[] + @ParameterizedTest + @EnumSource(ChronoUnit::class) + fun testWithEnumSource(unit: TemporalUnit) { + assertNotNull(unit) + } + // end::EnumSource_example[] + + // tag::EnumSource_example_autodetection[] + @ParameterizedTest + @EnumSource + fun testWithEnumSourceWithAutoDetection(unit: ChronoUnit) { + assertNotNull(unit) + } + // end::EnumSource_example_autodetection[] + + // tag::EnumSource_include_example[] + @ParameterizedTest + @EnumSource(names = ["DAYS", "HOURS"]) + fun testWithEnumSourceInclude(unit: ChronoUnit) { + assertTrue(EnumSet.of(ChronoUnit.DAYS, ChronoUnit.HOURS).contains(unit)) + } + // end::EnumSource_include_example[] + + // tag::EnumSource_range_example[] + @ParameterizedTest + @EnumSource(from = "HOURS", to = "DAYS") + fun testWithEnumSourceRange(unit: ChronoUnit) { + assertTrue(EnumSet.of(ChronoUnit.HOURS, ChronoUnit.HALF_DAYS, ChronoUnit.DAYS).contains(unit)) + } + // end::EnumSource_range_example[] + + // tag::EnumSource_exclude_example[] + @ParameterizedTest + @EnumSource(mode = EXCLUDE, names = ["ERAS", "FOREVER"]) + fun testWithEnumSourceExclude(unit: ChronoUnit) { + assertFalse(EnumSet.of(ChronoUnit.ERAS, ChronoUnit.FOREVER).contains(unit)) + } + // end::EnumSource_exclude_example[] + + // tag::EnumSource_regex_example[] + @ParameterizedTest + @EnumSource(mode = MATCH_ALL, names = ["^.*DAYS$"]) + fun testWithEnumSourceRegex(unit: ChronoUnit) { + assertTrue(unit.name.endsWith("DAYS")) + } + // end::EnumSource_regex_example[] + + // tag::EnumSource_range_exclude_example[] + @ParameterizedTest + @EnumSource(from = "HOURS", to = "DAYS", mode = EXCLUDE, names = ["HALF_DAYS"]) + fun testWithEnumSourceRangeExclude(unit: ChronoUnit) { + assertTrue(EnumSet.of(ChronoUnit.HOURS, ChronoUnit.DAYS).contains(unit)) + assertFalse(EnumSet.of(ChronoUnit.HALF_DAYS).contains(unit)) + } + // end::EnumSource_range_exclude_example[] + + // tag::simple_MethodSource_example[] + @ParameterizedTest + @MethodSource("stringProvider") + fun testWithExplicitLocalMethodSource(argument: String) { + assertNotNull(argument) + } + // end::simple_MethodSource_example[] + + // tag::simple_MethodSource_without_value_example[] + @ParameterizedTest + @MethodSource + fun testWithDefaultLocalMethodSource(argument: String) { + assertNotNull(argument) + } + // end::simple_MethodSource_without_value_example[] + + // tag::primitive_MethodSource_example[] + @ParameterizedTest + @MethodSource("range") + fun testWithRangeMethodSource(argument: Int) { + assertNotEquals(9, argument) + } + // end::primitive_MethodSource_example[] + + // tag::multi_arg_MethodSource_example[] + @ParameterizedTest + @MethodSource("stringIntAndListProvider") + fun testWithMultiArgMethodSource( + str: String, + num: Int, + list: List + ) { + assertEquals(5, str.length) + assertTrue(num in 1..2) + assertEquals(2, list.size) + } + // end::multi_arg_MethodSource_example[] + + // tag::default_field_FieldSource_example[] + @ParameterizedTest + @FieldSource + fun arrayOfFruits(fruit: String) { + assertFruit(fruit) + } + // end::default_field_FieldSource_example[] + + // tag::explicit_field_FieldSource_example[] + @ParameterizedTest + @FieldSource("listOfFruits") + fun singleFieldSource(fruit: String) { + assertFruit(fruit) + } + // end::explicit_field_FieldSource_example[] + + // tag::multiple_fields_FieldSource_example[] + @ParameterizedTest + @FieldSource("listOfFruits", "additionalFruits") + fun multipleFieldSources(fruit: String) { + assertFruit(fruit) + } + // end::multiple_fields_FieldSource_example[] + + // tag::named_arguments_FieldSource_example[] + @ParameterizedTest + @FieldSource + fun namedArgumentsSupplier(fruit: String) { + assertFruit(fruit) + } + // end::named_arguments_FieldSource_example[] + + private fun assertFruit(fruit: String) { + assertTrue(listOf("apple", "banana", "cherry", "dewberry").contains(fruit)) + } + + // tag::multi_arg_FieldSource_example[] + @ParameterizedTest + @FieldSource("stringIntAndListArguments") + fun testWithMultiArgFieldSource( + str: String, + num: Int, + list: List + ) { + assertEquals(5, str.length) + assertTrue(num in 1..2) + assertEquals(2, list.size) + } + // end::multi_arg_FieldSource_example[] + + // tag::CsvSource_example[] + @ParameterizedTest + @CsvSource( + "apple, 1", + "banana, 2", + "'lemon, lime', 0xF1", + "strawberry, 700_000" + ) + fun testWithCsvSource( + fruit: String, + rank: Int + ) { + assertNotNull(fruit) + assertNotEquals(0, rank) + } + // end::CsvSource_example[] + + // tag::CsvFileSource_example[] + @ParameterizedTest + @CsvFileSource(resources = ["/two-column.csv"], numLinesToSkip = 1) + fun testWithCsvFileSourceFromClasspath( + country: String, + reference: Int + ) { + assertNotNull(country) + assertNotEquals(0, reference) + } + + @ParameterizedTest + @CsvFileSource(files = ["src/test/resources/two-column.csv"], numLinesToSkip = 1) + fun testWithCsvFileSourceFromFile( + country: String, + reference: Int + ) { + assertNotNull(country) + assertNotEquals(0, reference) + } + + @ParameterizedTest + @CsvFileSource(resources = ["/two-column.csv"], useHeadersInDisplayName = true) + fun testWithCsvFileSourceAndHeaders( + country: String, + reference: Int + ) { + assertNotNull(country) + assertNotEquals(0, reference) + } + // end::CsvFileSource_example[] + + // tag::ArgumentsSource_example[] + @ParameterizedTest + @ArgumentsSource(MyArgumentsProvider::class) + fun testWithArgumentsSource(argument: String) { + assertNotNull(argument) + } + + // end::ArgumentsSource_example[] + // tag::ArgumentsProvider_example[] + class MyArgumentsProvider : ArgumentsProvider { + override fun provideArguments( + parameters: ParameterDeclarations, + context: ExtensionContext + ): Stream = Stream.of("apple", "banana").map(Arguments::of) + } + // end::ArgumentsProvider_example[] + + @ParameterizedTest + @ArgumentsSource(MyArgumentsProviderWithConstructorInjection::class) + fun testWithArgumentsSourceWithConstructorInjection(argument: String) { + assertNotNull(argument) + } + + // tag::ArgumentsProviderWithConstructorInjection_example[] + class MyArgumentsProviderWithConstructorInjection( + private val testInfo: TestInfo + ) : ArgumentsProvider { + override fun provideArguments( + parameters: ParameterDeclarations, + context: ExtensionContext + ): Stream = Stream.of(arguments(testInfo.displayName)) + } + // end::ArgumentsProviderWithConstructorInjection_example[] + + // tag::ParameterResolver_example[] + @BeforeEach + fun beforeEach(testInfo: TestInfo) { + // ... + } + + @ParameterizedTest + @ValueSource(strings = ["apple"]) + fun testWithRegularParameterResolver( + argument: String, + testReporter: TestReporter + ) { + testReporter.publishEntry("argument", argument) + } + + @AfterEach + fun afterEach(testInfo: TestInfo) { + // ... + } + // end::ParameterResolver_example[] + + // tag::implicit_conversion_example[] + @ParameterizedTest + @ValueSource(strings = ["SECONDS"]) + fun testWithImplicitArgumentConversion(argument: ChronoUnit) { + assertNotNull(argument.name) + } + // end::implicit_conversion_example[] + + // tag::implicit_fallback_conversion_example[] + @ParameterizedTest + @ValueSource(strings = ["42 Cats"]) + fun testWithImplicitFallbackArgumentConversion(book: Book) { + assertEquals("42 Cats", book.title) + } + + // end::implicit_fallback_conversion_example[] + // tag::implicit_fallback_conversion_example_Book[] + class Book private constructor( + val title: String + ) { + companion object { + @JvmStatic + fun fromTitle(title: String): Book = Book(title) + } + } + // end::implicit_fallback_conversion_example_Book[] + + // tag::explicit_conversion_example[] + @ParameterizedTest + @EnumSource(ChronoUnit::class) + fun testWithExplicitArgumentConversion( + @ConvertWith(ToStringArgumentConverter::class) argument: String + ) { + assertNotNull(ChronoUnit.valueOf(argument)) + } + + // end::explicit_conversion_example[] + // tag::explicit_conversion_example_ToStringArgumentConverter[] + class ToStringArgumentConverter : SimpleArgumentConverter() { + override fun convert( + source: Any?, + targetType: Class<*> + ): Any { + assertEquals(String::class.java, targetType, "Can only convert to String") + return if (source is Enum<*>) source.name else source.toString() + } + } + // end::explicit_conversion_example_ToStringArgumentConverter[] + + // tag::explicit_conversion_example_TypedArgumentConverter[] + class ToLengthArgumentConverter : + TypedArgumentConverter( + String::class.java, + Int::class.javaObjectType + ) { + override fun convert(source: String?): Int = source?.length ?: 0 + } + // end::explicit_conversion_example_TypedArgumentConverter[] + + // tag::explicit_java_time_converter[] + @ParameterizedTest + @ValueSource(strings = ["01.01.2017", "31.12.2017"]) + fun testWithExplicitJavaTimeConverter( + @JavaTimeConversionPattern("dd.MM.yyyy") argument: LocalDate + ) { + assertEquals(2017, argument.year) + } + // end::explicit_java_time_converter[] + + // tag::ArgumentsAccessor_example[] + @ParameterizedTest + @CsvSource( + "Jane, Doe, F, 1990-05-20", + "John, Doe, M, 1990-10-22" + ) + fun testWithArgumentsAccessor(arguments: ArgumentsAccessor) { + val person = + Person( + arguments.getString(0), + arguments.getString(1), + arguments.get(2, Gender::class.java), + arguments.get(3, LocalDate::class.java) + ) + + if (person.firstName == "Jane") { + assertEquals(Gender.F, person.gender) + } else { + assertEquals(Gender.M, person.gender) + } + assertEquals("Doe", person.lastName) + assertEquals(1990, person.dateOfBirth.year) + } + // end::ArgumentsAccessor_example[] + + // tag::ArgumentsAggregator_example[] + @ParameterizedTest + @CsvSource( + "Jane, Doe, F, 1990-05-20", + "John, Doe, M, 1990-10-22" + ) + fun testWithArgumentsAggregator( + @AggregateWith(PersonAggregator::class) person: Person + ) { + // perform assertions against person + } + + // end::ArgumentsAggregator_example[] + // tag::ArgumentsAggregator_example_PersonAggregator[] + class PersonAggregator : SimpleArgumentsAggregator() { + override fun aggregateArguments( + arguments: ArgumentsAccessor, + targetType: Class<*>, + context: AnnotatedElementContext, + parameterIndex: Int + ): Person = + Person( + arguments.getString(0), + arguments.getString(1), + arguments.get(2, Gender::class.java), + arguments.get(3, LocalDate::class.java) + ) + } + // end::ArgumentsAggregator_example_PersonAggregator[] + + // tag::ArgumentsAggregator_with_custom_annotation_example[] + @ParameterizedTest + @CsvSource( + "Jane, Doe, F, 1990-05-20", + "John, Doe, M, 1990-10-22" + ) + fun testWithCustomAggregatorAnnotation( + @CsvToPerson person: Person + ) { + // perform assertions against person + } + // end::ArgumentsAggregator_with_custom_annotation_example[] + + // tag::ArgumentsAggregator_with_custom_annotation_example_CsvToPerson[] + @Retention(AnnotationRetention.RUNTIME) + @Target(AnnotationTarget.VALUE_PARAMETER) + @AggregateWith(PersonAggregator::class) + annotation class CsvToPerson + // end::ArgumentsAggregator_with_custom_annotation_example_CsvToPerson[] + + // tag::custom_display_names[] + @DisplayName("Display name of container") + @ParameterizedTest(name = "{index} ==> the rank of {0} is {1}") + @CsvSource("apple, 1", "banana, 2", "'lemon, lime', 3") + fun testWithCustomDisplayNames( + fruit: String, + rank: Int + ) { + } + // end::custom_display_names[] + + // tag::named_arguments[] + @DisplayName("A parameterized test with named arguments") + @ParameterizedTest(name = "{index}: {0}") + @MethodSource("namedArguments") + fun testWithNamedArguments(file: File) { + } + // end::named_arguments[] + + // tag::named_argument_set[] + @DisplayName("A parameterized test with named argument sets") + @ParameterizedTest + @FieldSource("argumentSets") + fun testWithArgumentSets( + file1: File, + file2: File + ) { + } + // end::named_argument_set[] + + // tag::repeatable_annotations[] + @DisplayName("A parameterized test that makes use of repeatable annotations") + @ParameterizedTest + @MethodSource("someProvider") + @MethodSource("otherProvider") + fun testWithRepeatedAnnotation(argument: String) { + assertNotNull(argument) + } + // end::repeatable_annotations[] + + @Disabled("Fails prior to invoking the test method") + // tag::argument_count_validation[] + @ParameterizedTest(argumentCountValidation = ArgumentCountValidationMode.STRICT) + @CsvSource("42, -666") + fun testWithArgumentCountValidation(number: Int) { + assertTrue(number > 0) + } + // end::argument_count_validation[] +} + +// tag::stringProvider[] +fun stringProvider(): Stream = Stream.of("apple", "banana") +// end::stringProvider[] + +// tag::testWithDefaultLocalMethodSource_provider[] +fun testWithDefaultLocalMethodSource(): Stream = Stream.of("apple", "banana") +// end::testWithDefaultLocalMethodSource_provider[] + +// tag::range[] +fun range(): IntStream = IntStream.range(0, 20).skip(10) +// end::range[] + +// tag::stringIntAndListProvider[] +fun stringIntAndListProvider(): Stream = + Stream.of( + arguments("apple", 1, listOf("a", "b")), + arguments("lemon", 2, listOf("x", "y")) + ) +// end::stringIntAndListProvider[] + +// tag::arrayOfFruits[] +val arrayOfFruits = arrayOf("apple", "banana") +// end::arrayOfFruits[] + +// tag::listOfFruits[] +val listOfFruits = listOf("apple", "banana") +// end::listOfFruits[] + +// tag::additionalFruits[] +val additionalFruits = listOf("cherry", "dewberry") +// end::additionalFruits[] + +// tag::namedArgumentsSupplier[] +val namedArgumentsSupplier: Supplier> = + Supplier { + Stream.of( + arguments(named("Apple", "apple")), + arguments(named("Banana", "banana")) + ) + } +// end::namedArgumentsSupplier[] + +// tag::stringIntAndListArguments[] +var stringIntAndListArguments = + listOf( + arguments("apple", 1, listOf("a", "b")), + arguments("lemon", 2, listOf("x", "y")) + ) +// end::stringIntAndListArguments[] + +// tag::namedArguments[] +fun namedArguments(): Stream = + Stream.of( + arguments(named("An important file", File("path1"))), + arguments(named("Another file", File("path2"))) + ) +// end::namedArguments[] + +// tag::argumentSets[] +val argumentSets = + listOf( + Arguments.argumentSet("Important files", File("path1"), File("path2")), + Arguments.argumentSet("Other files", File("path3"), File("path4")) + ) +// end::argumentSets[] + +// tag::someProvider[] +fun someProvider(): Stream = Stream.of("foo") +// end::someProvider[] + +// tag::otherProvider[] +fun otherProvider(): Stream = Stream.of("bar") +// end::otherProvider[] diff --git a/documentation/src/test/kotlin/example/kotlin/TaggingDemo.kt b/documentation/src/test/kotlin/example/kotlin/TaggingDemo.kt new file mode 100644 index 000000000000..1027dfe6a420 --- /dev/null +++ b/documentation/src/test/kotlin/example/kotlin/TaggingDemo.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2015-2026 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 example.kotlin + +// tag::user_guide[] +import org.junit.jupiter.api.Tag +import org.junit.jupiter.api.Test + +@Tag("fast") +@Tag("model") +class TaggingDemo { + @Test + @Tag("taxes") + fun testingTaxCalculation() { + } +} +// end::user_guide[] From a029788622c57e32e1c00143949a07124de327c7 Mon Sep 17 00:00:00 2001 From: Danil Pavlov Date: Thu, 16 Jul 2026 16:10:07 +0200 Subject: [PATCH 2/7] Fix Kotlin includes and examples Signed-off-by: Danil Pavlov --- .../parameterized-classes-and-tests.adoc | 115 +++++++----------- .../example/kotlin/ExternalFieldSourceDemo.kt | 7 +- .../kotlin/ExternalMethodSourceDemo.kt | 10 +- .../MethodSourceParameterResolutionDemo.kt | 25 ++-- .../example/kotlin/ParameterizedClassDemo.kt | 2 +- .../kotlin/ParameterizedLifecycleDemo.kt | 49 +++----- .../example/kotlin/ParameterizedRecordDemo.kt | 4 +- .../example/kotlin/ParameterizedTestDemo.kt | 21 ++-- 8 files changed, 99 insertions(+), 134 deletions(-) diff --git a/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc b/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc index 56f34448316c..77925f5c5907 100644 --- a/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc +++ b/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc @@ -634,11 +634,11 @@ include::example$java/example/ParameterizedTestDemo.java[tags=simple_MethodSourc Kotlin:: + -- -[source,kotlin,indent=0] +[source,kotlin] ---- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=simple_MethodSource_example] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=simple_MethodSource_example,indent=0] -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=stringProvider] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=stringProvider,indent=0] ---- -- ==== @@ -662,14 +662,11 @@ include::example$java/example/ParameterizedTestDemo.java[tags=simple_MethodSourc Kotlin:: + -- -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=simple_MethodSource_without_value_example] +[source,kotlin] ---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=simple_MethodSource_without_value_example,indent=0] -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=testWithDefaultLocalMethodSource_provider] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=testWithDefaultLocalMethodSource_provider,indent=0] ---- -- ==== @@ -691,9 +688,11 @@ include::example$java/example/ParameterizedTestDemo.java[tags=primitive_MethodSo Kotlin:: + -- -[source,kotlin,indent=0] +[source,kotlin] ---- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[lines=189..193;567] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=primitive_MethodSource_example,indent=0] + +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=range,indent=0] ---- -- ==== @@ -719,9 +718,11 @@ include::example$java/example/ParameterizedTestDemo.java[tags=multi_arg_MethodSo Kotlin:: + -- -[source,kotlin,indent=0] +[source,kotlin] ---- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=multi_arg_MethodSource_example;stringIntAndListProvider] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=multi_arg_MethodSource_example,indent=0] + +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=stringIntAndListProvider,indent=0] ---- -- ==== @@ -775,9 +776,11 @@ include::example$java/example/MethodSourceParameterResolutionDemo.java[tags=para Kotlin:: + -- -[source,kotlin,indent=0] +[source,kotlin] ---- -include::example$kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt[tags=parameter_resolution_MethodSource_example] +include::example$kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt[tags=parameter_resolution_MethodSource_example,indent=0] + +include::example$kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt[tags=parameter_resolution_factory_MethodSource_example,indent=0] ---- -- ==== @@ -847,14 +850,11 @@ include::example$java/example/ParameterizedTestDemo.java[tags=default_field_Fiel Kotlin:: + -- -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=default_field_FieldSource_example] +[source,kotlin] ---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=default_field_FieldSource_example,indent=0] -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=arrayOfFruits] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=arrayOfFruits,indent=0] ---- -- ==== @@ -877,14 +877,11 @@ include::example$java/example/ParameterizedTestDemo.java[tags=explicit_field_Fie Kotlin:: + -- -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=explicit_field_FieldSource_example] +[source,kotlin] ---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=explicit_field_FieldSource_example,indent=0] -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=listOfFruits] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=listOfFruits,indent=0] ---- -- ==== @@ -909,19 +906,13 @@ include::example$java/example/ParameterizedTestDemo.java[tags=multiple_fields_Fi Kotlin:: + -- -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=multiple_fields_FieldSource_example] +[source,kotlin] ---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=multiple_fields_FieldSource_example,indent=0] -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=listOfFruits] ----- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=listOfFruits,indent=0] -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=additionalFruits] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=additionalFruits,indent=0] ---- -- ==== @@ -947,14 +938,11 @@ include::example$java/example/ParameterizedTestDemo.java[tags=named_arguments_Fi Kotlin:: + -- -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=named_arguments_FieldSource_example] +[source,kotlin] ---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=named_arguments_FieldSource_example,indent=0] -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=namedArgumentsSupplier] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=namedArgumentsSupplier,indent=0] ---- -- ==== @@ -987,14 +975,11 @@ include::example$java/example/ParameterizedTestDemo.java[tags=multi_arg_FieldSou Kotlin:: + -- -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=multi_arg_FieldSource_example] +[source,kotlin] ---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=multi_arg_FieldSource_example,indent=0] -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=stringIntAndListArguments] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=stringIntAndListArguments,indent=0] ---- -- ==== @@ -1407,19 +1392,13 @@ include::example$java/example/ParameterizedTestDemo.java[tags=repeatable_annotat Kotlin:: + -- -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=repeatable_annotations] +[source,kotlin] ---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=repeatable_annotations,indent=0] -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=someProvider] ----- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=someProvider,indent=0] -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=otherProvider] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=otherProvider,indent=0] ---- -- ==== @@ -1997,14 +1976,11 @@ include::example$java/example/ParameterizedTestDemo.java[tags=named_arguments] Kotlin:: + -- -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=named_arguments] +[source,kotlin] ---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=named_arguments,indent=0] -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=namedArguments] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=namedArguments,indent=0] ---- -- ==== @@ -2046,14 +2022,11 @@ include::example$java/example/ParameterizedTestDemo.java[tags=named_argument_set Kotlin:: + -- -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=named_argument_set] +[source,kotlin] ---- +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=named_argument_set,indent=0] -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=argumentSets] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=argumentSets,indent=0] ---- -- ==== diff --git a/documentation/src/test/kotlin/example/kotlin/ExternalFieldSourceDemo.kt b/documentation/src/test/kotlin/example/kotlin/ExternalFieldSourceDemo.kt index 2b49207a85f4..810a1785883b 100644 --- a/documentation/src/test/kotlin/example/kotlin/ExternalFieldSourceDemo.kt +++ b/documentation/src/test/kotlin/example/kotlin/ExternalFieldSourceDemo.kt @@ -16,9 +16,14 @@ import org.junit.jupiter.params.provider.FieldSource class ExternalFieldSourceDemo { // tag::external_field_FieldSource_example[] @ParameterizedTest - @FieldSource("example.FruitUtils#tropicalFruits") + @FieldSource("example.kotlin.FruitUtils#tropicalFruits") fun testWithExternalFieldSource(tropicalFruit: String) { // test with tropicalFruit } // end::external_field_FieldSource_example[] } + +object FruitUtils { + @JvmField + val tropicalFruits: List = listOf("pineapple", "kiwi") +} diff --git a/documentation/src/test/kotlin/example/kotlin/ExternalMethodSourceDemo.kt b/documentation/src/test/kotlin/example/kotlin/ExternalMethodSourceDemo.kt index cfd656e16c47..02ba3e6208c5 100644 --- a/documentation/src/test/kotlin/example/kotlin/ExternalMethodSourceDemo.kt +++ b/documentation/src/test/kotlin/example/kotlin/ExternalMethodSourceDemo.kt @@ -8,17 +8,23 @@ * https://www.eclipse.org/legal/epl-v20.html */ +// tag::external_MethodSource_example[] package example.kotlin -// tag::external_MethodSource_example[] import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource +import java.util.stream.Stream class ExternalMethodSourceDemo { @ParameterizedTest - @MethodSource("example.StringsProviders#tinyStrings") + @MethodSource("example.kotlin.StringsProviders#tinyStrings") fun testWithExternalMethodSource(tinyString: String) { // test with tiny string } } + +object StringsProviders { + @JvmStatic + fun tinyStrings(): Stream = Stream.of(".", "oo", "OOO") +} // end::external_MethodSource_example[] diff --git a/documentation/src/test/kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt b/documentation/src/test/kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt index 01fd844e9e7b..ff3676255a10 100644 --- a/documentation/src/test/kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt +++ b/documentation/src/test/kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt @@ -23,6 +23,10 @@ import java.util.stream.Stream class MethodSourceParameterResolutionDemo { // tag::parameter_resolution_MethodSource_example[] + @JvmField + @RegisterExtension + val integerResolver = IntegerResolver() + @ParameterizedTest @MethodSource("factoryMethodWithArguments") fun testWithFactoryMethodWithArguments(argument: String) { @@ -41,17 +45,12 @@ class MethodSourceParameterResolutionDemo { ): Any = 2 } // end::parameter_resolution_MethodSource_example[] - - companion object { - @JvmField - @RegisterExtension - val integerResolver = IntegerResolver() - - @JvmStatic - fun factoryMethodWithArguments(quantity: Int): Stream = - Stream.of( - arguments("$quantity apples"), - arguments("$quantity lemons") - ) - } } + +// tag::parameter_resolution_factory_MethodSource_example[] +fun factoryMethodWithArguments(quantity: Int): Stream = + Stream.of( + arguments("$quantity apples"), + arguments("$quantity lemons") + ) +// end::parameter_resolution_factory_MethodSource_example[] diff --git a/documentation/src/test/kotlin/example/kotlin/ParameterizedClassDemo.kt b/documentation/src/test/kotlin/example/kotlin/ParameterizedClassDemo.kt index ef864c8a70a8..42c7c809ad8b 100644 --- a/documentation/src/test/kotlin/example/kotlin/ParameterizedClassDemo.kt +++ b/documentation/src/test/kotlin/example/kotlin/ParameterizedClassDemo.kt @@ -126,7 +126,7 @@ class ParameterizedClassDemo { private fun assertFruit(fruit: String) { assertTrue( - listOf("apple", "banana", "cherry", "dewberry").contains(fruit) + fruit in listOf("apple", "banana", "cherry", "dewberry") ) { "not a fruit: $fruit" } } diff --git a/documentation/src/test/kotlin/example/kotlin/ParameterizedLifecycleDemo.kt b/documentation/src/test/kotlin/example/kotlin/ParameterizedLifecycleDemo.kt index b5f6f5f98dc5..d3c981415f06 100644 --- a/documentation/src/test/kotlin/example/kotlin/ParameterizedLifecycleDemo.kt +++ b/documentation/src/test/kotlin/example/kotlin/ParameterizedLifecycleDemo.kt @@ -1,5 +1,5 @@ /* - * Copyright 2026 the original author or authors. + * Copyright 2015-2026 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 @@ -10,16 +10,6 @@ @file:JvmName("ParameterizedLifecycleDemo") -/* - * Copyright 2015-2026 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 example.kotlin import org.junit.jupiter.api.Assertions.assertEquals @@ -35,24 +25,6 @@ import java.nio.file.Files import java.nio.file.Path // tag::example[] -class TextFile( - val fileName: String, - val content: String -) { - var path: Path? = null - - override fun toString(): String = fileName -} - -fun textFiles(): List = - listOf( - // tag::custom_line_break[] - TextFile("file1", "first content"), - // tag::custom_line_break[] - TextFile("file2", "second content") -// tag::custom_line_break[] - ) - @ParameterizedClass @MethodSource("textFiles") class TextFileTests { @@ -73,7 +45,7 @@ class TextFileTests { @JvmStatic @AfterParameterizedClassInvocation fun afterInvocation(textFile: TextFile) { // <3> - val actualContent = Files.readString(textFile.path!!) + val actualContent = Files.readString(textFile.path) assertEquals(textFile.content, actualContent, "Content must not have changed") // Custom cleanup logic, if necessary // File will be deleted automatically by @TempDir support @@ -82,7 +54,7 @@ class TextFileTests { @Test fun test() { - assertTrue(Files.exists(textFile.path!!)) // <2> + assertTrue(Files.exists(textFile.path)) // <2> } @Test @@ -90,4 +62,19 @@ class TextFileTests { // ... } } + +class TextFile( + val fileName: String, + val content: String +) { + lateinit var path: Path + + override fun toString(): String = fileName +} + +fun textFiles(): List = + listOf( + TextFile("file1", "first content"), + TextFile("file2", "second content") + ) // end::example[] diff --git a/documentation/src/test/kotlin/example/kotlin/ParameterizedRecordDemo.kt b/documentation/src/test/kotlin/example/kotlin/ParameterizedRecordDemo.kt index a86fce2ea2e8..676b7240be49 100644 --- a/documentation/src/test/kotlin/example/kotlin/ParameterizedRecordDemo.kt +++ b/documentation/src/test/kotlin/example/kotlin/ParameterizedRecordDemo.kt @@ -17,8 +17,8 @@ import org.junit.jupiter.params.ParameterizedClass import org.junit.jupiter.params.provider.CsvSource class ParameterizedRecordDemo { - @Nested // tag::example[] + @Nested @ParameterizedClass @CsvSource("apple, 23", "banana, 42") inner class FruitTests( @@ -39,7 +39,7 @@ class ParameterizedRecordDemo { // end::example[] private fun assertFruit(fruit: String) { - assertTrue(listOf("apple", "banana", "cherry", "dewberry").contains(fruit)) + assertTrue(fruit in listOf("apple", "banana", "cherry", "dewberry")) } private fun assertQuantity(quantity: Int) { diff --git a/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt b/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt index e7753aff6958..18d7d23faf02 100644 --- a/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt +++ b/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt @@ -132,7 +132,7 @@ class ParameterizedTestDemo { @ParameterizedTest @EnumSource(names = ["DAYS", "HOURS"]) fun testWithEnumSourceInclude(unit: ChronoUnit) { - assertTrue(EnumSet.of(ChronoUnit.DAYS, ChronoUnit.HOURS).contains(unit)) + assertTrue(unit in EnumSet.of(ChronoUnit.DAYS, ChronoUnit.HOURS)) } // end::EnumSource_include_example[] @@ -140,7 +140,7 @@ class ParameterizedTestDemo { @ParameterizedTest @EnumSource(from = "HOURS", to = "DAYS") fun testWithEnumSourceRange(unit: ChronoUnit) { - assertTrue(EnumSet.of(ChronoUnit.HOURS, ChronoUnit.HALF_DAYS, ChronoUnit.DAYS).contains(unit)) + assertTrue(unit in EnumSet.of(ChronoUnit.HOURS, ChronoUnit.HALF_DAYS, ChronoUnit.DAYS)) } // end::EnumSource_range_example[] @@ -148,7 +148,7 @@ class ParameterizedTestDemo { @ParameterizedTest @EnumSource(mode = EXCLUDE, names = ["ERAS", "FOREVER"]) fun testWithEnumSourceExclude(unit: ChronoUnit) { - assertFalse(EnumSet.of(ChronoUnit.ERAS, ChronoUnit.FOREVER).contains(unit)) + assertFalse(unit in EnumSet.of(ChronoUnit.ERAS, ChronoUnit.FOREVER)) } // end::EnumSource_exclude_example[] @@ -164,8 +164,8 @@ class ParameterizedTestDemo { @ParameterizedTest @EnumSource(from = "HOURS", to = "DAYS", mode = EXCLUDE, names = ["HALF_DAYS"]) fun testWithEnumSourceRangeExclude(unit: ChronoUnit) { - assertTrue(EnumSet.of(ChronoUnit.HOURS, ChronoUnit.DAYS).contains(unit)) - assertFalse(EnumSet.of(ChronoUnit.HALF_DAYS).contains(unit)) + assertTrue(unit in EnumSet.of(ChronoUnit.HOURS, ChronoUnit.DAYS)) + assertFalse(unit in EnumSet.of(ChronoUnit.HALF_DAYS)) } // end::EnumSource_range_exclude_example[] @@ -240,7 +240,7 @@ class ParameterizedTestDemo { // end::named_arguments_FieldSource_example[] private fun assertFruit(fruit: String) { - assertTrue(listOf("apple", "banana", "cherry", "dewberry").contains(fruit)) + assertTrue(fruit in listOf("apple", "banana", "cherry", "dewberry")) } // tag::multi_arg_FieldSource_example[] @@ -378,14 +378,9 @@ class ParameterizedTestDemo { // end::implicit_fallback_conversion_example[] // tag::implicit_fallback_conversion_example_Book[] - class Book private constructor( + class Book( val title: String - ) { - companion object { - @JvmStatic - fun fromTitle(title: String): Book = Book(title) - } - } + ) // end::implicit_fallback_conversion_example_Book[] // tag::explicit_conversion_example[] From 15ca98361defc5bbb8bba8ca20679e92fa1c3808 Mon Sep 17 00:00:00 2001 From: Marc Philipp Date: Sat, 25 Jul 2026 08:02:25 +0200 Subject: [PATCH 3/7] Fix formatting Signed-off-by: Danil Pavlov --- .../src/test/kotlin/example/kotlin/ExternalMethodSourceDemo.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/test/kotlin/example/kotlin/ExternalMethodSourceDemo.kt b/documentation/src/test/kotlin/example/kotlin/ExternalMethodSourceDemo.kt index 02ba3e6208c5..02ad1d99fa76 100644 --- a/documentation/src/test/kotlin/example/kotlin/ExternalMethodSourceDemo.kt +++ b/documentation/src/test/kotlin/example/kotlin/ExternalMethodSourceDemo.kt @@ -8,9 +8,9 @@ * https://www.eclipse.org/legal/epl-v20.html */ -// tag::external_MethodSource_example[] package example.kotlin +// tag::external_MethodSource_example[] import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource import java.util.stream.Stream From 3dc3554a7304111d80cc3bcb1307000e5fd0a9f4 Mon Sep 17 00:00:00 2001 From: Danil Pavlov Date: Mon, 27 Jul 2026 14:30:14 +0200 Subject: [PATCH 4/7] Fix inner class output and record example Signed-off-by: Danil Pavlov --- .../parameterized-classes-and-tests.adoc | 18 +------ .../example/kotlin/ParameterizedClassDemo.kt | 36 ++++++++++++-- .../example/kotlin/ParameterizedRecordDemo.kt | 48 ------------------- 3 files changed, 33 insertions(+), 69 deletions(-) delete mode 100644 documentation/src/test/kotlin/example/kotlin/ParameterizedRecordDemo.kt diff --git a/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc b/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc index 77925f5c5907..0ac3f69faefb 100644 --- a/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc +++ b/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc @@ -169,29 +169,13 @@ include::example$kotlin/example/kotlin/ParameterizedClassDemo.kt[tags=constructo -- ==== -You may use _records_ to implement parameterized classes that avoid the boilerplate code +In Java, you can use _records_ to implement parameterized classes that avoid the boilerplate code of declaring a test class constructor. -[tabs] -==== -Java:: -+ --- [source,java,indent=0] ---- include::example$java/example/ParameterizedRecordDemo.java[tags=example] ---- --- - -Kotlin:: -+ --- -[source,kotlin,indent=0] ----- -include::example$kotlin/example/kotlin/ParameterizedRecordDemo.kt[tags=example] ----- --- -==== [[consuming-arguments-field-injection]] ==== Field Injection diff --git a/documentation/src/test/kotlin/example/kotlin/ParameterizedClassDemo.kt b/documentation/src/test/kotlin/example/kotlin/ParameterizedClassDemo.kt index 42c7c809ad8b..15d582fd1560 100644 --- a/documentation/src/test/kotlin/example/kotlin/ParameterizedClassDemo.kt +++ b/documentation/src/test/kotlin/example/kotlin/ParameterizedClassDemo.kt @@ -25,11 +25,18 @@ import org.junit.jupiter.params.provider.ValueSource import java.time.Duration class ParameterizedClassDemo { + // @formatter:off @Nested // tag::first_example[] @ParameterizedClass @ValueSource(strings = ["racecar", "radar", "able was I ere I saw elba"]) - inner class PalindromeTests { + // end::first_example[] + inner + // tag::first_example[] + class PalindromeTests { + // end::first_example[] + // @formatter:on + // tag::first_example[] @Parameter lateinit var candidate: String @@ -48,14 +55,21 @@ class ParameterizedClassDemo { @Nested inner class ConstructorInjection { + // @formatter:off @Nested // tag::constructor_injection[] @ParameterizedClass @CsvSource("apple, 23", "banana, 42") - inner class FruitTests( + // end::constructor_injection[] + inner + // tag::constructor_injection[] + class FruitTests( private val fruit: String, private val quantity: Int ) { + // end::constructor_injection[] + // @formatter:on + // tag::constructor_injection[] @Test fun test() { assertFruit(fruit) @@ -72,11 +86,18 @@ class ParameterizedClassDemo { @Nested inner class FieldInjection { + // @formatter:off @Nested // tag::field_injection[] @ParameterizedClass @CsvSource("apple, 23", "banana, 42") - inner class FruitTests { + // end::field_injection[] + inner + // tag::field_injection[] + class FruitTests { + // end::field_injection[] + // @formatter:on + // tag::field_injection[] @Parameter(0) lateinit var fruit: String @@ -97,12 +118,19 @@ class ParameterizedClassDemo { // end::field_injection[] } + // @formatter:off @Nested // tag::nested[] @Execution(SAME_THREAD) @ParameterizedClass @ValueSource(strings = ["apple", "banana"]) - inner class FruitTests { + // end::nested[] + inner + // tag::nested[] + class FruitTests { + // end::nested[] + // @formatter:on + // tag::nested[] @Parameter lateinit var fruit: String diff --git a/documentation/src/test/kotlin/example/kotlin/ParameterizedRecordDemo.kt b/documentation/src/test/kotlin/example/kotlin/ParameterizedRecordDemo.kt deleted file mode 100644 index 676b7240be49..000000000000 --- a/documentation/src/test/kotlin/example/kotlin/ParameterizedRecordDemo.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2015-2026 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 example.kotlin - -import org.junit.jupiter.api.Assertions.assertTrue -import org.junit.jupiter.api.Nested -import org.junit.jupiter.api.Test -import org.junit.jupiter.params.ParameterizedClass -import org.junit.jupiter.params.provider.CsvSource - -class ParameterizedRecordDemo { - // tag::example[] - @Nested - @ParameterizedClass - @CsvSource("apple, 23", "banana, 42") - inner class FruitTests( - private val fruit: String, - private val quantity: Int - ) { - @Test - fun test() { - assertFruit(fruit) - assertQuantity(quantity) - } - - @Test - fun anotherTest() { - // ... - } - } - // end::example[] - - private fun assertFruit(fruit: String) { - assertTrue(fruit in listOf("apple", "banana", "cherry", "dewberry")) - } - - private fun assertQuantity(quantity: Int) { - assertTrue(quantity >= 0) - } -} From 5f6f6abce4b49e01fee7eb89e2a71b08c1d20b2b Mon Sep 17 00:00:00 2001 From: Danil Pavlov Date: Thu, 30 Jul 2026 13:48:00 +0200 Subject: [PATCH 5/7] Fix Stream and consoleLauncherTest Signed-off-by: Danil Pavlov --- .../kotlin/ExternalMethodSourceDemo.kt | 3 +-- .../MethodSourceParameterResolutionDemo.kt | 6 ++---- .../example/kotlin/ParameterizedTestDemo.kt | 19 +++++++++---------- 3 files changed, 12 insertions(+), 16 deletions(-) diff --git a/documentation/src/test/kotlin/example/kotlin/ExternalMethodSourceDemo.kt b/documentation/src/test/kotlin/example/kotlin/ExternalMethodSourceDemo.kt index 02ad1d99fa76..da0b739a301f 100644 --- a/documentation/src/test/kotlin/example/kotlin/ExternalMethodSourceDemo.kt +++ b/documentation/src/test/kotlin/example/kotlin/ExternalMethodSourceDemo.kt @@ -13,7 +13,6 @@ package example.kotlin // tag::external_MethodSource_example[] import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.MethodSource -import java.util.stream.Stream class ExternalMethodSourceDemo { @ParameterizedTest @@ -25,6 +24,6 @@ class ExternalMethodSourceDemo { object StringsProviders { @JvmStatic - fun tinyStrings(): Stream = Stream.of(".", "oo", "OOO") + fun tinyStrings() = sequenceOf(".", "oo", "OOO") } // end::external_MethodSource_example[] diff --git a/documentation/src/test/kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt b/documentation/src/test/kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt index ff3676255a10..c29b14274979 100644 --- a/documentation/src/test/kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt +++ b/documentation/src/test/kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt @@ -16,10 +16,8 @@ import org.junit.jupiter.api.extension.ParameterContext import org.junit.jupiter.api.extension.ParameterResolver import org.junit.jupiter.api.extension.RegisterExtension import org.junit.jupiter.params.ParameterizedTest -import org.junit.jupiter.params.provider.Arguments import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource -import java.util.stream.Stream class MethodSourceParameterResolutionDemo { // tag::parameter_resolution_MethodSource_example[] @@ -48,8 +46,8 @@ class MethodSourceParameterResolutionDemo { } // tag::parameter_resolution_factory_MethodSource_example[] -fun factoryMethodWithArguments(quantity: Int): Stream = - Stream.of( +fun factoryMethodWithArguments(quantity: Int) = + sequenceOf( arguments("$quantity apples"), arguments("$quantity lemons") ) diff --git a/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt b/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt index 18d7d23faf02..3a094457af86 100644 --- a/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt +++ b/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt @@ -61,7 +61,6 @@ import java.time.temporal.ChronoUnit import java.time.temporal.TemporalUnit import java.util.EnumSet import java.util.function.Supplier -import java.util.stream.IntStream import java.util.stream.Stream @Execution(SAME_THREAD) @@ -551,20 +550,20 @@ class ParameterizedTestDemo { } // tag::stringProvider[] -fun stringProvider(): Stream = Stream.of("apple", "banana") +fun stringProvider() = sequenceOf("apple", "banana") // end::stringProvider[] // tag::testWithDefaultLocalMethodSource_provider[] -fun testWithDefaultLocalMethodSource(): Stream = Stream.of("apple", "banana") +fun testWithDefaultLocalMethodSource() = sequenceOf("apple", "banana") // end::testWithDefaultLocalMethodSource_provider[] // tag::range[] -fun range(): IntStream = IntStream.range(0, 20).skip(10) +fun range() = 10..<20 // end::range[] // tag::stringIntAndListProvider[] -fun stringIntAndListProvider(): Stream = - Stream.of( +fun stringIntAndListProvider() = + sequenceOf( arguments("apple", 1, listOf("a", "b")), arguments("lemon", 2, listOf("x", "y")) ) @@ -601,8 +600,8 @@ var stringIntAndListArguments = // end::stringIntAndListArguments[] // tag::namedArguments[] -fun namedArguments(): Stream = - Stream.of( +fun namedArguments() = + sequenceOf( arguments(named("An important file", File("path1"))), arguments(named("Another file", File("path2"))) ) @@ -617,9 +616,9 @@ val argumentSets = // end::argumentSets[] // tag::someProvider[] -fun someProvider(): Stream = Stream.of("foo") +fun someProvider() = sequenceOf("foo") // end::someProvider[] // tag::otherProvider[] -fun otherProvider(): Stream = Stream.of("bar") +fun otherProvider() = sequenceOf("bar") // end::otherProvider[] From 6aa25affa883b9845aa75db2341ffb2061a4d094 Mon Sep 17 00:00:00 2001 From: Danil Pavlov Date: Thu, 30 Jul 2026 16:28:44 +0200 Subject: [PATCH 6/7] Fix consoleLauncherTest with PER_CLASS Signed-off-by: Danil Pavlov --- .../parameterized-classes-and-tests.adoc | 82 ++++-------- .../MethodSourceParameterResolutionDemo.kt | 17 +-- .../kotlin/ParameterizedLifecycleDemo.kt | 49 ++++--- .../example/kotlin/ParameterizedTestDemo.kt | 125 +++++++----------- 4 files changed, 109 insertions(+), 164 deletions(-) diff --git a/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc b/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc index 0ac3f69faefb..623121983c75 100644 --- a/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc +++ b/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc @@ -618,11 +618,9 @@ include::example$java/example/ParameterizedTestDemo.java[tags=simple_MethodSourc Kotlin:: + -- -[source,kotlin] +[source,kotlin,indent=0] ---- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=simple_MethodSource_example,indent=0] - -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=stringProvider,indent=0] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=simple_MethodSource_example] ---- -- ==== @@ -646,11 +644,9 @@ include::example$java/example/ParameterizedTestDemo.java[tags=simple_MethodSourc Kotlin:: + -- -[source,kotlin] +[source,kotlin,indent=0] ---- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=simple_MethodSource_without_value_example,indent=0] - -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=testWithDefaultLocalMethodSource_provider,indent=0] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=simple_MethodSource_without_value_example] ---- -- ==== @@ -672,11 +668,9 @@ include::example$java/example/ParameterizedTestDemo.java[tags=primitive_MethodSo Kotlin:: + -- -[source,kotlin] +[source,kotlin,indent=0] ---- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=primitive_MethodSource_example,indent=0] - -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=range,indent=0] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=primitive_MethodSource_example] ---- -- ==== @@ -702,11 +696,9 @@ include::example$java/example/ParameterizedTestDemo.java[tags=multi_arg_MethodSo Kotlin:: + -- -[source,kotlin] +[source,kotlin,indent=0] ---- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=multi_arg_MethodSource_example,indent=0] - -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=stringIntAndListProvider,indent=0] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=multi_arg_MethodSource_example] ---- -- ==== @@ -760,11 +752,9 @@ include::example$java/example/MethodSourceParameterResolutionDemo.java[tags=para Kotlin:: + -- -[source,kotlin] +[source,kotlin,indent=0] ---- -include::example$kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt[tags=parameter_resolution_MethodSource_example,indent=0] - -include::example$kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt[tags=parameter_resolution_factory_MethodSource_example,indent=0] +include::example$kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt[tags=parameter_resolution_MethodSource_example] ---- -- ==== @@ -834,11 +824,9 @@ include::example$java/example/ParameterizedTestDemo.java[tags=default_field_Fiel Kotlin:: + -- -[source,kotlin] +[source,kotlin,indent=0] ---- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=default_field_FieldSource_example,indent=0] - -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=arrayOfFruits,indent=0] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=default_field_FieldSource_example] ---- -- ==== @@ -861,11 +849,9 @@ include::example$java/example/ParameterizedTestDemo.java[tags=explicit_field_Fie Kotlin:: + -- -[source,kotlin] +[source,kotlin,indent=0] ---- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=explicit_field_FieldSource_example,indent=0] - -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=listOfFruits,indent=0] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=explicit_field_FieldSource_example] ---- -- ==== @@ -890,13 +876,9 @@ include::example$java/example/ParameterizedTestDemo.java[tags=multiple_fields_Fi Kotlin:: + -- -[source,kotlin] +[source,kotlin,indent=0] ---- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=multiple_fields_FieldSource_example,indent=0] - -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=listOfFruits,indent=0] - -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=additionalFruits,indent=0] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=multiple_fields_FieldSource_example] ---- -- ==== @@ -922,11 +904,9 @@ include::example$java/example/ParameterizedTestDemo.java[tags=named_arguments_Fi Kotlin:: + -- -[source,kotlin] +[source,kotlin,indent=0] ---- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=named_arguments_FieldSource_example,indent=0] - -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=namedArgumentsSupplier,indent=0] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=named_arguments_FieldSource_example] ---- -- ==== @@ -959,11 +939,9 @@ include::example$java/example/ParameterizedTestDemo.java[tags=multi_arg_FieldSou Kotlin:: + -- -[source,kotlin] +[source,kotlin,indent=0] ---- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=multi_arg_FieldSource_example,indent=0] - -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=stringIntAndListArguments,indent=0] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=multi_arg_FieldSource_example] ---- -- ==== @@ -1376,13 +1354,9 @@ include::example$java/example/ParameterizedTestDemo.java[tags=repeatable_annotat Kotlin:: + -- -[source,kotlin] +[source,kotlin,indent=0] ---- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=repeatable_annotations,indent=0] - -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=someProvider,indent=0] - -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=otherProvider,indent=0] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=repeatable_annotations] ---- -- ==== @@ -1960,11 +1934,9 @@ include::example$java/example/ParameterizedTestDemo.java[tags=named_arguments] Kotlin:: + -- -[source,kotlin] +[source,kotlin,indent=0] ---- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=named_arguments,indent=0] - -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=namedArguments,indent=0] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=named_arguments] ---- -- ==== @@ -2006,11 +1978,9 @@ include::example$java/example/ParameterizedTestDemo.java[tags=named_argument_set Kotlin:: + -- -[source,kotlin] +[source,kotlin,indent=0] ---- -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=named_argument_set,indent=0] - -include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=argumentSets,indent=0] +include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=named_argument_set] ---- -- ==== diff --git a/documentation/src/test/kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt b/documentation/src/test/kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt index c29b14274979..dd9a7c49088b 100644 --- a/documentation/src/test/kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt +++ b/documentation/src/test/kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt @@ -11,6 +11,8 @@ package example.kotlin import org.junit.jupiter.api.Assertions.assertTrue +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS import org.junit.jupiter.api.extension.ExtensionContext import org.junit.jupiter.api.extension.ParameterContext import org.junit.jupiter.api.extension.ParameterResolver @@ -19,6 +21,7 @@ import org.junit.jupiter.params.ParameterizedTest import org.junit.jupiter.params.provider.Arguments.arguments import org.junit.jupiter.params.provider.MethodSource +@TestInstance(PER_CLASS) class MethodSourceParameterResolutionDemo { // tag::parameter_resolution_MethodSource_example[] @JvmField @@ -31,6 +34,12 @@ class MethodSourceParameterResolutionDemo { assertTrue(argument.startsWith("2")) } + fun factoryMethodWithArguments(quantity: Int) = + sequenceOf( + arguments("$quantity apples"), + arguments("$quantity lemons") + ) + class IntegerResolver : ParameterResolver { override fun supportsParameter( parameterContext: ParameterContext, @@ -44,11 +53,3 @@ class MethodSourceParameterResolutionDemo { } // end::parameter_resolution_MethodSource_example[] } - -// tag::parameter_resolution_factory_MethodSource_example[] -fun factoryMethodWithArguments(quantity: Int) = - sequenceOf( - arguments("$quantity apples"), - arguments("$quantity lemons") - ) -// end::parameter_resolution_factory_MethodSource_example[] diff --git a/documentation/src/test/kotlin/example/kotlin/ParameterizedLifecycleDemo.kt b/documentation/src/test/kotlin/example/kotlin/ParameterizedLifecycleDemo.kt index d3c981415f06..8230272db63a 100644 --- a/documentation/src/test/kotlin/example/kotlin/ParameterizedLifecycleDemo.kt +++ b/documentation/src/test/kotlin/example/kotlin/ParameterizedLifecycleDemo.kt @@ -8,13 +8,13 @@ * https://www.eclipse.org/legal/epl-v20.html */ -@file:JvmName("ParameterizedLifecycleDemo") - package example.kotlin import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS import org.junit.jupiter.api.io.TempDir import org.junit.jupiter.params.AfterParameterizedClassInvocation import org.junit.jupiter.params.BeforeParameterizedClassInvocation @@ -27,29 +27,32 @@ import java.nio.file.Path // tag::example[] @ParameterizedClass @MethodSource("textFiles") +@TestInstance(PER_CLASS) class TextFileTests { @Parameter lateinit var textFile: TextFile - companion object { - @JvmStatic - @BeforeParameterizedClassInvocation - fun beforeInvocation( - textFile: TextFile, - @TempDir tempDir: Path - ) { // <1> - val filePath = tempDir.resolve(textFile.fileName) - textFile.path = Files.writeString(filePath, textFile.content) - } + fun textFiles(): List = + listOf( + TextFile("file1", "first content"), + TextFile("file2", "second content") + ) + + @BeforeParameterizedClassInvocation + fun beforeInvocation( + textFile: TextFile, + @TempDir tempDir: Path + ) { // <1> + val filePath = tempDir.resolve(textFile.fileName) + textFile.path = Files.writeString(filePath, textFile.content) + } - @JvmStatic - @AfterParameterizedClassInvocation - fun afterInvocation(textFile: TextFile) { // <3> - val actualContent = Files.readString(textFile.path) - assertEquals(textFile.content, actualContent, "Content must not have changed") - // Custom cleanup logic, if necessary - // File will be deleted automatically by @TempDir support - } + @AfterParameterizedClassInvocation + fun afterInvocation(textFile: TextFile) { // <3> + val actualContent = Files.readString(textFile.path) + assertEquals(textFile.content, actualContent, "Content must not have changed") + // Custom cleanup logic, if necessary + // File will be deleted automatically by @TempDir support } @Test @@ -71,10 +74,4 @@ class TextFile( override fun toString(): String = fileName } - -fun textFiles(): List = - listOf( - TextFile("file1", "first content"), - TextFile("file2", "second content") - ) // end::example[] diff --git a/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt b/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt index 3a094457af86..d59e43b43a6e 100644 --- a/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt +++ b/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt @@ -25,6 +25,8 @@ import org.junit.jupiter.api.DisplayName import org.junit.jupiter.api.Named.named import org.junit.jupiter.api.Nested import org.junit.jupiter.api.TestInfo +import org.junit.jupiter.api.TestInstance +import org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS import org.junit.jupiter.api.TestReporter import org.junit.jupiter.api.extension.AnnotatedElementContext import org.junit.jupiter.api.extension.ExtensionContext @@ -64,6 +66,7 @@ import java.util.function.Supplier import java.util.stream.Stream @Execution(SAME_THREAD) +@TestInstance(PER_CLASS) class ParameterizedTestDemo { @BeforeEach fun printDisplayName(testInfo: TestInfo) { @@ -174,6 +177,8 @@ class ParameterizedTestDemo { fun testWithExplicitLocalMethodSource(argument: String) { assertNotNull(argument) } + + fun stringProvider() = sequenceOf("apple", "banana") // end::simple_MethodSource_example[] // tag::simple_MethodSource_without_value_example[] @@ -182,6 +187,8 @@ class ParameterizedTestDemo { fun testWithDefaultLocalMethodSource(argument: String) { assertNotNull(argument) } + + fun testWithDefaultLocalMethodSource() = sequenceOf("apple", "banana") // end::simple_MethodSource_without_value_example[] // tag::primitive_MethodSource_example[] @@ -190,6 +197,8 @@ class ParameterizedTestDemo { fun testWithRangeMethodSource(argument: Int) { assertNotEquals(9, argument) } + + fun range() = 10..<20 // end::primitive_MethodSource_example[] // tag::multi_arg_MethodSource_example[] @@ -204,6 +213,12 @@ class ParameterizedTestDemo { assertTrue(num in 1..2) assertEquals(2, list.size) } + + fun stringIntAndListProvider() = + sequenceOf( + arguments("apple", 1, listOf("a", "b")), + arguments("lemon", 2, listOf("x", "y")) + ) // end::multi_arg_MethodSource_example[] // tag::default_field_FieldSource_example[] @@ -212,6 +227,8 @@ class ParameterizedTestDemo { fun arrayOfFruits(fruit: String) { assertFruit(fruit) } + + val arrayOfFruits = arrayOf("apple", "banana") // end::default_field_FieldSource_example[] // tag::explicit_field_FieldSource_example[] @@ -220,6 +237,8 @@ class ParameterizedTestDemo { fun singleFieldSource(fruit: String) { assertFruit(fruit) } + + val listOfFruits = listOf("apple", "banana") // end::explicit_field_FieldSource_example[] // tag::multiple_fields_FieldSource_example[] @@ -228,6 +247,8 @@ class ParameterizedTestDemo { fun multipleFieldSources(fruit: String) { assertFruit(fruit) } + + val additionalFruits = listOf("cherry", "dewberry") // end::multiple_fields_FieldSource_example[] // tag::named_arguments_FieldSource_example[] @@ -236,6 +257,14 @@ class ParameterizedTestDemo { fun namedArgumentsSupplier(fruit: String) { assertFruit(fruit) } + + val namedArgumentsSupplier: Supplier> = + Supplier { + Stream.of( + arguments(named("Apple", "apple")), + arguments(named("Banana", "banana")) + ) + } // end::named_arguments_FieldSource_example[] private fun assertFruit(fruit: String) { @@ -254,6 +283,12 @@ class ParameterizedTestDemo { assertTrue(num in 1..2) assertEquals(2, list.size) } + + var stringIntAndListArguments = + listOf( + arguments("apple", 1, listOf("a", "b")), + arguments("lemon", 2, listOf("x", "y")) + ) // end::multi_arg_FieldSource_example[] // tag::CsvSource_example[] @@ -516,6 +551,12 @@ class ParameterizedTestDemo { @MethodSource("namedArguments") fun testWithNamedArguments(file: File) { } + + fun namedArguments() = + sequenceOf( + arguments(named("An important file", File("path1"))), + arguments(named("Another file", File("path2"))) + ) // end::named_arguments[] // tag::named_argument_set[] @@ -527,6 +568,12 @@ class ParameterizedTestDemo { file2: File ) { } + + val argumentSets = + listOf( + Arguments.argumentSet("Important files", File("path1"), File("path2")), + Arguments.argumentSet("Other files", File("path3"), File("path4")) + ) // end::named_argument_set[] // tag::repeatable_annotations[] @@ -537,6 +584,10 @@ class ParameterizedTestDemo { fun testWithRepeatedAnnotation(argument: String) { assertNotNull(argument) } + + fun someProvider() = sequenceOf("foo") + + fun otherProvider() = sequenceOf("bar") // end::repeatable_annotations[] @Disabled("Fails prior to invoking the test method") @@ -548,77 +599,3 @@ class ParameterizedTestDemo { } // end::argument_count_validation[] } - -// tag::stringProvider[] -fun stringProvider() = sequenceOf("apple", "banana") -// end::stringProvider[] - -// tag::testWithDefaultLocalMethodSource_provider[] -fun testWithDefaultLocalMethodSource() = sequenceOf("apple", "banana") -// end::testWithDefaultLocalMethodSource_provider[] - -// tag::range[] -fun range() = 10..<20 -// end::range[] - -// tag::stringIntAndListProvider[] -fun stringIntAndListProvider() = - sequenceOf( - arguments("apple", 1, listOf("a", "b")), - arguments("lemon", 2, listOf("x", "y")) - ) -// end::stringIntAndListProvider[] - -// tag::arrayOfFruits[] -val arrayOfFruits = arrayOf("apple", "banana") -// end::arrayOfFruits[] - -// tag::listOfFruits[] -val listOfFruits = listOf("apple", "banana") -// end::listOfFruits[] - -// tag::additionalFruits[] -val additionalFruits = listOf("cherry", "dewberry") -// end::additionalFruits[] - -// tag::namedArgumentsSupplier[] -val namedArgumentsSupplier: Supplier> = - Supplier { - Stream.of( - arguments(named("Apple", "apple")), - arguments(named("Banana", "banana")) - ) - } -// end::namedArgumentsSupplier[] - -// tag::stringIntAndListArguments[] -var stringIntAndListArguments = - listOf( - arguments("apple", 1, listOf("a", "b")), - arguments("lemon", 2, listOf("x", "y")) - ) -// end::stringIntAndListArguments[] - -// tag::namedArguments[] -fun namedArguments() = - sequenceOf( - arguments(named("An important file", File("path1"))), - arguments(named("Another file", File("path2"))) - ) -// end::namedArguments[] - -// tag::argumentSets[] -val argumentSets = - listOf( - Arguments.argumentSet("Important files", File("path1"), File("path2")), - Arguments.argumentSet("Other files", File("path3"), File("path4")) - ) -// end::argumentSets[] - -// tag::someProvider[] -fun someProvider() = sequenceOf("foo") -// end::someProvider[] - -// tag::otherProvider[] -fun otherProvider() = sequenceOf("bar") -// end::otherProvider[] From 0c2db3e69f0a5867acf156e6d7e1f94c7b33d3ec Mon Sep 17 00:00:00 2001 From: Danil Pavlov Date: Fri, 31 Jul 2026 19:36:56 +0200 Subject: [PATCH 7/7] Add notes for PER_CLASS Signed-off-by: Danil Pavlov --- .../parameterized-classes-and-tests.adoc | 30 +++++++++++++++++++ .../example/kotlin/ParameterizedTestDemo.kt | 9 ++++-- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc b/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc index 623121983c75..875699141308 100644 --- a/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc +++ b/documentation/modules/ROOT/pages/writing-tests/parameterized-classes-and-tests.adoc @@ -622,6 +622,8 @@ Kotlin:: ---- include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=simple_MethodSource_example] ---- + +NOTE: For conciseness, this snippet assumes that the enclosing test class uses the `PER_CLASS` xref:writing-tests/test-instance-lifecycle.adoc[]. -- ==== @@ -648,6 +650,8 @@ Kotlin:: ---- include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=simple_MethodSource_without_value_example] ---- + +NOTE: For conciseness, this snippet assumes that the enclosing test class uses the `PER_CLASS` xref:writing-tests/test-instance-lifecycle.adoc[]. -- ==== @@ -672,6 +676,8 @@ Kotlin:: ---- include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=primitive_MethodSource_example] ---- + +NOTE: For conciseness, this snippet assumes that the enclosing test class uses the `PER_CLASS` xref:writing-tests/test-instance-lifecycle.adoc[]. -- ==== @@ -700,6 +706,8 @@ Kotlin:: ---- include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=multi_arg_MethodSource_example] ---- + +NOTE: For conciseness, this snippet assumes that the enclosing test class uses the `PER_CLASS` xref:writing-tests/test-instance-lifecycle.adoc[]. -- ==== @@ -724,6 +732,8 @@ Kotlin:: -- [source,kotlin,indent=0] ---- +package example.kotlin + include::example$kotlin/example/kotlin/ExternalMethodSourceDemo.kt[tags=external_MethodSource_example] ---- -- @@ -756,6 +766,8 @@ Kotlin:: ---- include::example$kotlin/example/kotlin/MethodSourceParameterResolutionDemo.kt[tags=parameter_resolution_MethodSource_example] ---- + +NOTE: For conciseness, this snippet assumes that the enclosing test class uses the `PER_CLASS` xref:writing-tests/test-instance-lifecycle.adoc[]. -- ==== @@ -828,6 +840,8 @@ Kotlin:: ---- include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tags=default_field_FieldSource_example] ---- + +NOTE: For conciseness, this snippet assumes that the enclosing test class uses the `PER_CLASS` xref:writing-tests/test-instance-lifecycle.adoc[]. -- ==== @@ -853,6 +867,8 @@ Kotlin:: ---- include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=explicit_field_FieldSource_example] ---- + +NOTE: For conciseness, this snippet assumes that the enclosing test class uses the `PER_CLASS` xref:writing-tests/test-instance-lifecycle.adoc[]. -- ==== @@ -880,6 +896,8 @@ Kotlin:: ---- include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=multiple_fields_FieldSource_example] ---- + +NOTE: For conciseness, this snippet assumes that the enclosing test class uses the `PER_CLASS` xref:writing-tests/test-instance-lifecycle.adoc[]. -- ==== @@ -908,6 +926,8 @@ Kotlin:: ---- include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=named_arguments_FieldSource_example] ---- + +NOTE: For conciseness, this snippet assumes that the enclosing test class uses the `PER_CLASS` xref:writing-tests/test-instance-lifecycle.adoc[]. -- ==== @@ -943,6 +963,8 @@ Kotlin:: ---- include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=multi_arg_FieldSource_example] ---- + +NOTE: For conciseness, this snippet assumes that the enclosing test class uses the `PER_CLASS` xref:writing-tests/test-instance-lifecycle.adoc[]. -- ==== @@ -1358,6 +1380,8 @@ Kotlin:: ---- include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=repeatable_annotations] ---- + +NOTE: For conciseness, this snippet assumes that the enclosing test class uses the `PER_CLASS` xref:writing-tests/test-instance-lifecycle.adoc[]. -- ==== @@ -1938,6 +1962,8 @@ Kotlin:: ---- include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=named_arguments] ---- + +NOTE: For conciseness, this snippet assumes that the enclosing test class uses the `PER_CLASS` xref:writing-tests/test-instance-lifecycle.adoc[]. -- ==== @@ -1982,6 +2008,8 @@ Kotlin:: ---- include::example$kotlin/example/kotlin/ParameterizedTestDemo.kt[tag=named_argument_set] ---- + +NOTE: For conciseness, this snippet assumes that the enclosing test class uses the `PER_CLASS` xref:writing-tests/test-instance-lifecycle.adoc[]. -- ==== @@ -2170,6 +2198,8 @@ Kotlin:: ---- include::example$kotlin/example/kotlin/ParameterizedLifecycleDemo.kt[tags=example] ---- + +NOTE: For conciseness, this snippet uses the `PER_CLASS` xref:writing-tests/test-instance-lifecycle.adoc[]. -- ==== <1> Initialization of the argument _before_ each invocation of the parameterized class diff --git a/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt b/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt index d59e43b43a6e..dffe53343343 100644 --- a/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt +++ b/documentation/src/test/kotlin/example/kotlin/ParameterizedTestDemo.kt @@ -412,9 +412,14 @@ class ParameterizedTestDemo { // end::implicit_fallback_conversion_example[] // tag::implicit_fallback_conversion_example_Book[] - class Book( + class Book private constructor( val title: String - ) + ) { + companion object { + @JvmStatic + fun fromTitle(title: String): Book = Book(title) + } + } // end::implicit_fallback_conversion_example_Book[] // tag::explicit_conversion_example[]