From 2e56d56363948b7e37fdc92dab8f153c42f05aa1 Mon Sep 17 00:00:00 2001 From: jjebing Date: Fri, 4 Oct 2024 22:28:05 +0200 Subject: [PATCH 01/19] Issue: #3708 add ParameterizedTest#argumentCountValidation This allows parameterized tests to fail when there are more arguments provided than declared by the test method. This is done in a backwards compatible way by only enabling that validation when the new `junit.jupiter.params.argumentCountValidation` is set to `strict` or `ParameterizedTest#argumentCountValidation` is set to `ArgumentCountValidationMode.STRICT`. --- .../params/ArgumentCountValidationMode.java | 52 ++++++++++++++++ .../jupiter/params/ParameterizedTest.java | 18 ++++++ .../params/ParameterizedTestExtension.java | 59 +++++++++++++++++++ .../ParameterizedTestIntegrationTests.java | 37 ++++++++++++ 4 files changed, 166 insertions(+) create mode 100644 junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidationMode.java diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidationMode.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidationMode.java new file mode 100644 index 000000000000..3921019637c9 --- /dev/null +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidationMode.java @@ -0,0 +1,52 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.params; + +import org.apiguardian.api.API; +import org.junit.jupiter.params.provider.ArgumentsSource; + +/** + * Enumeration of argument count validation modes for {@link ParameterizedTest @ParameterizedTest}. + * + *

When an {@link ArgumentsSource} provides more arguments than declared by the test method, + * there might be a bug in the test method or the {@link ArgumentsSource}. + * By default, the additional arguments are ignored. + * {@link ArgumentCountValidationMode} allows you to control how additional arguments are handled. + * + * @since 5.12 + * @see ParameterizedTest + */ +@API(status = API.Status.EXPERIMENTAL, since = "5.12") +public enum ArgumentCountValidationMode { + /** + * Use the default cleanup mode. + * + *

The default cleanup mode may be changed via the + * {@value ParameterizedTestExtension#ARGUMENT_COUNT_VALIDATION_KEY} configuration parameter + * (see the User Guide for details on configuration parameters). + */ + DEFAULT, + + /** + * Use the "none" argument count validation mode. + * + *

When there are more arguments provided than declared by the test method, + * these additional arguments are ignored. + */ + NONE, + + /** + * Use the strict argument count validation mode. + * + *

When there are more arguments provided than declared by the test method, this raises an error. + */ + STRICT, +} diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTest.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTest.java index bf45c87fd71b..a714e81ff016 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTest.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTest.java @@ -22,6 +22,7 @@ import org.apiguardian.api.API; import org.junit.jupiter.api.TestTemplate; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.provider.ArgumentsSource; /** * {@code @ParameterizedTest} is used to signal that the annotated method is a @@ -291,4 +292,21 @@ @API(status = STABLE, since = "5.10") boolean autoCloseArguments() default true; + /** + * Configure how the number of arguments provided by an {@link ArgumentsSource} are validated. + * + *

Defaults to {@link ArgumentCountValidationMode#DEFAULT}. + * + *

When an {@link ArgumentsSource} provides more arguments than declared by the test method, + * there might be a bug in the test method or the {@link ArgumentsSource}. + * By default, the additional arguments are ignored. + * {@code argumentCountValidation} allows you to control how additional arguments are handled. + * This can also be controlled via the {@value ParameterizedTestExtension#ARGUMENT_COUNT_VALIDATION_KEY} + * configuration parameter (see the User Guide for details on configuration parameters). + * + * @since 5.12 + * @see ArgumentCountValidationMode + */ + @API(status = EXPERIMENTAL, since = "5.12") + ArgumentCountValidationMode argumentCountValidation() default ArgumentCountValidationMode.DEFAULT; } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java index aefbba1f2adc..1a81f47234fc 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java @@ -15,9 +15,13 @@ import static org.junit.platform.commons.support.AnnotationSupport.isAnnotated; import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.NoSuchElementException; +import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Stream; +import org.junit.jupiter.api.extension.ExtensionConfigurationException; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext.Namespace; import org.junit.jupiter.api.extension.TestTemplateInvocationContext; @@ -26,6 +30,8 @@ import org.junit.jupiter.params.provider.ArgumentsProvider; import org.junit.jupiter.params.provider.ArgumentsSource; import org.junit.jupiter.params.support.AnnotationConsumerInitializer; +import org.junit.platform.commons.logging.Logger; +import org.junit.platform.commons.logging.LoggerFactory; import org.junit.platform.commons.util.ExceptionUtils; import org.junit.platform.commons.util.Preconditions; @@ -34,10 +40,13 @@ */ class ParameterizedTestExtension implements TestTemplateInvocationContextProvider { + private static final Logger logger = LoggerFactory.getLogger(ParameterizedTestExtension.class); + private static final String METHOD_CONTEXT_KEY = "context"; static final String ARGUMENT_MAX_LENGTH_KEY = "junit.jupiter.params.displayname.argument.maxlength"; static final String DEFAULT_DISPLAY_NAME = "{default_display_name}"; static final String DISPLAY_NAME_PATTERN_KEY = "junit.jupiter.params.displayname.default"; + static final String ARGUMENT_COUNT_VALIDATION_KEY = "junit.jupiter.params.argumentCountValidation"; @Override public boolean supportsTestTemplate(ExtensionContext context) { @@ -86,6 +95,7 @@ public Stream provideTestTemplateInvocationContex .map(provider -> AnnotationConsumerInitializer.initialize(templateMethod, provider)) .flatMap(provider -> arguments(provider, extensionContext)) .map(arguments -> { + validateArgumentCount(extensionContext, arguments); invocationCount.incrementAndGet(); return createInvocationContext(formatter, methodContext, arguments, invocationCount.intValue()); }) @@ -99,6 +109,55 @@ private ExtensionContext.Store getStore(ExtensionContext context) { return context.getStore(Namespace.create(ParameterizedTestExtension.class, context.getRequiredTestMethod())); } + private void validateArgumentCount(ExtensionContext extensionContext, Arguments arguments) { + ArgumentCountValidationMode argumentCountValidationMode = getArgumentCountValidationMode(extensionContext); + switch (argumentCountValidationMode) { + case DEFAULT: + case NONE: + return; + case STRICT: + int testParamCount = extensionContext.getRequiredTestMethod().getParameterCount(); + int argumentsCount = arguments.get().length; + Preconditions.condition(testParamCount == argumentsCount, () -> String.format( + "Configuration error: the @ParameterizedTest has %s argument(s) but there were %s argument(s) provided./nNote: the provided arguments are %s", + testParamCount, argumentsCount, Arrays.toString(arguments.get()))); + break; + default: + throw new ExtensionConfigurationException( + "Unsupported argument count validation mode: " + argumentCountValidationMode); + } + } + + private ArgumentCountValidationMode getArgumentCountValidationMode(ExtensionContext extensionContext) { + ParameterizedTest parameterizedTest = findAnnotation(// + extensionContext.getRequiredTestMethod(), ParameterizedTest.class// + ).orElseThrow(NoSuchElementException::new); + if (parameterizedTest.argumentCountValidation() != ArgumentCountValidationMode.DEFAULT) { + return parameterizedTest.argumentCountValidation(); + } + else { + return getArgumentCountValidationModeConfiguration(extensionContext); + } + } + + private ArgumentCountValidationMode getArgumentCountValidationModeConfiguration(ExtensionContext extensionContext) { + String key = ARGUMENT_COUNT_VALIDATION_KEY; + ArgumentCountValidationMode fallback = ArgumentCountValidationMode.DEFAULT; + Optional optionalValue = extensionContext.getConfigurationParameter(key); + if (optionalValue.isPresent()) { + String value = optionalValue.get(); + return Arrays.stream(ArgumentCountValidationMode.values()).filter( + mode -> mode.name().equalsIgnoreCase(value)).findFirst().orElseGet(() -> { + logger.warn(() -> String.format( + "Ignored invalid configuration '%s' set via the '%s' configuration parameter.", value, key)); + return fallback; + }); + } + else { + return fallback; + } + } + private TestTemplateInvocationContext createInvocationContext(ParameterizedTestNameFormatter formatter, ParameterizedTestMethodContext methodContext, Arguments arguments, int invocationIndex) { diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java index 4ff8ba90e1e5..1987a92bbe5e 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java @@ -115,6 +115,7 @@ import org.junit.platform.testkit.engine.EngineExecutionResults; import org.junit.platform.testkit.engine.EngineTestKit; import org.junit.platform.testkit.engine.Event; +import org.junit.platform.testkit.engine.EventConditions; import org.opentest4j.TestAbortedException; /** @@ -1093,6 +1094,42 @@ private EngineExecutionResults execute(String methodName, Class... methodPara } + @Nested + class UnusedArgumentsWithStrictArgumentsCountIntegrationTests { + @Test + void failsWithArgumentsSourceProvidingUnusedArguments() { + var results = execute(UnusedArgumentsTestCase.class, "testWithTwoUnusedStringArgumentsProvider", + String.class); + results.allEvents().assertThatEvents() // + .haveExactly(1, event(EventConditions.finishedWithFailure(message( + "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided./nNote: the provided arguments are [foo, unused1]")))); + } + + @Test + void failsWithMethodSourceProvidingUnusedArguments() { + var results = execute(UnusedArgumentsTestCase.class, "testWithMethodSourceProvidingUnusedArguments", + String.class); + results.allEvents().assertThatEvents() // + .haveExactly(1, event(EventConditions.finishedWithFailure(message( + "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided./nNote: the provided arguments are [foo, unused1]")))); + } + + @Test + void executesWithMethodSourceProvidingUnusedArguments() { + var results = execute(RepeatableSourcesTestCase.class, "testWithRepeatableCsvSource", String.class); + results.allEvents().assertThatEvents() // + .haveExactly(1, event(test(), displayName("[1] argument=a"), finishedWithFailure(message("a")))) // + .haveExactly(1, event(test(), displayName("[2] argument=b"), finishedWithFailure(message("b")))); + } + + private EngineExecutionResults execute(Class javaClass, String methodName, + Class... methodParameterTypes) { + return EngineTestKit.engine(new JupiterTestEngine()).selectors( + selectMethod(javaClass, methodName, methodParameterTypes)).configurationParameter( + ParameterizedTestExtension.ARGUMENT_COUNT_VALIDATION_KEY, "strict").execute(); + } + } + @Nested class RepeatableSourcesIntegrationTests { From 98cdf69a9fd4aa996878539f0baecc90defe5f7d Mon Sep 17 00:00:00 2001 From: Jonas Jebing <34072795+JonasJebing@users.noreply.github.com> Date: Wed, 23 Oct 2024 16:18:18 +0200 Subject: [PATCH 02/19] address PR comments - add `writing-tests-parameterized-tests-argument-count-validation` section to the user guide. - cache configuration parameter in `ExtensionContext.Store` - adjust log and error messages Issue #3708 --- .../asciidoc/user-guide/writing-tests.adoc | 23 +++++++++ .../java/example/ParameterizedTestDemo.java | 9 ++++ .../params/ParameterizedTestExtension.java | 50 ++++++++++++------- .../ParameterizedTestIntegrationTests.java | 46 +++++++++++++---- 4 files changed, 102 insertions(+), 26 deletions(-) diff --git a/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc b/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc index 8df05f70c3ba..3f408ec55cc2 100644 --- a/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc +++ b/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc @@ -2020,6 +2020,29 @@ The following annotations are repeatable: * `@CsvFileSource` * `@ArgumentsSource` +[[writing-tests-parameterized-tests-argument-count-validation]] +==== Argument Count Validation + +WARNING: Argument count validation is currently an _experimental_ feature. You're invited to +give it a try and provide feedback to the JUnit team so they can improve and eventually +<> this feature. + +By default, when an arguments source provides more arguments than the test method needs, +those additional arguments are ignored and the test executes as usual. +This can lead to bugs where arguments are never passed to the parameterized test method. + +To prevent this, you can set argument count validation to 'strict'. +Then, any additional arguments will cause an error instead. + +To change this behavior for all tests, set the `junit.jupiter.params.argumentCountValidation` +<> to `strict`. +To change this behavior for a single test, +use the `argumentCountValidation` attribute of the `@ParameterizedTest` annotation: + +[source,java,indent=0] +---- +include::{testDir}/example/ParameterizedTestDemo.java[tags=argument_count_validation] +---- [[writing-tests-parameterized-tests-argument-conversion]] ==== Argument Conversion diff --git a/documentation/src/test/java/example/ParameterizedTestDemo.java b/documentation/src/test/java/example/ParameterizedTestDemo.java index 9027b86d67e4..3d1cc9691e67 100644 --- a/documentation/src/test/java/example/ParameterizedTestDemo.java +++ b/documentation/src/test/java/example/ParameterizedTestDemo.java @@ -51,6 +51,7 @@ import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.parallel.Execution; +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; @@ -607,4 +608,12 @@ static Stream otherProvider() { return Stream.of("bar"); } // end::repeatable_annotations[] + + // tag::argument_count_validation[] + @ParameterizedTest(argumentCountValidation = ArgumentCountValidationMode.STRICT) + @CsvSource({ "42, -666" }) + void testWithArgumentCountValidation(int number) { + assertTrue(number > 0); + } + // end::argument_count_validation[] } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java index e34ab1ae6117..f558736298f1 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java @@ -14,7 +14,6 @@ import static org.junit.platform.commons.support.AnnotationSupport.findRepeatableAnnotations; import java.lang.reflect.Method; -import java.util.Optional; import java.util.Arrays; import java.util.NoSuchElementException; import java.util.Optional; @@ -70,7 +69,7 @@ public boolean supportsTestTemplate(ExtensionContext context) { + "and before any arguments resolved by another ParameterResolver.", templateMethod.toGenericString())); - getStore(context).put(METHOD_CONTEXT_KEY, methodContext); + getStoreInMethodNamespace(context).put(METHOD_CONTEXT_KEY, methodContext); return true; } @@ -108,14 +107,18 @@ public boolean mayReturnZeroTestTemplateInvocationContexts(ExtensionContext exte } private ParameterizedTestMethodContext getMethodContext(ExtensionContext extensionContext) { - return getStore(extensionContext)// + return getStoreInMethodNamespace(extensionContext)// .get(METHOD_CONTEXT_KEY, ParameterizedTestMethodContext.class); } - private ExtensionContext.Store getStore(ExtensionContext context) { + private ExtensionContext.Store getStoreInMethodNamespace(ExtensionContext context) { return context.getStore(Namespace.create(ParameterizedTestExtension.class, context.getRequiredTestMethod())); } + private ExtensionContext.Store getStoreInExtensionNamespace(ExtensionContext context) { + return context.getStore(Namespace.create(ParameterizedTestExtension.class)); + } + private void validateArgumentCount(ExtensionContext extensionContext, Arguments arguments) { ArgumentCountValidationMode argumentCountValidationMode = getArgumentCountValidationMode(extensionContext); switch (argumentCountValidationMode) { @@ -126,7 +129,7 @@ private void validateArgumentCount(ExtensionContext extensionContext, Arguments int testParamCount = extensionContext.getRequiredTestMethod().getParameterCount(); int argumentsCount = arguments.get().length; Preconditions.condition(testParamCount == argumentsCount, () -> String.format( - "Configuration error: the @ParameterizedTest has %s argument(s) but there were %s argument(s) provided./nNote: the provided arguments are %s", + "Configuration error: the @ParameterizedTest has %s argument(s) but there were %s argument(s) provided.%nNote: the provided arguments are %s", testParamCount, argumentsCount, Arrays.toString(arguments.get()))); break; default: @@ -149,20 +152,33 @@ private ArgumentCountValidationMode getArgumentCountValidationMode(ExtensionCont private ArgumentCountValidationMode getArgumentCountValidationModeConfiguration(ExtensionContext extensionContext) { String key = ARGUMENT_COUNT_VALIDATION_KEY; - ArgumentCountValidationMode fallback = ArgumentCountValidationMode.DEFAULT; - Optional optionalValue = extensionContext.getConfigurationParameter(key); - if (optionalValue.isPresent()) { - String value = optionalValue.get(); - return Arrays.stream(ArgumentCountValidationMode.values()).filter( - mode -> mode.name().equalsIgnoreCase(value)).findFirst().orElseGet(() -> { + ArgumentCountValidationMode fallback = ArgumentCountValidationMode.NONE; + ExtensionContext.Store store = getStoreInExtensionNamespace(extensionContext); + return store.getOrComputeIfAbsent(key, k -> { + Optional optionalConfigValue = extensionContext.getConfigurationParameter(key); + if (optionalConfigValue.isPresent()) { + String configValue = optionalConfigValue.get(); + Optional enumValue = Arrays.stream( + ArgumentCountValidationMode.values()).filter( + mode -> mode.name().equalsIgnoreCase(configValue)).findFirst(); + if (enumValue.isPresent()) { + logger.config(() -> String.format( + "Using ArgumentCountValidationMode '%s' set via the '%s' configuration parameter.", + enumValue.get().name(), key)); + return enumValue.get(); + } + else { logger.warn(() -> String.format( - "Ignored invalid configuration '%s' set via the '%s' configuration parameter.", value, key)); + "Invalid ArgumentCountValidationMode '%s' set via the '%s' configuration parameter. " + + "Falling back to the %s default value.", + configValue, key, fallback.name())); return fallback; - }); - } - else { - return fallback; - } + } + } + else { + return fallback; + } + }, ArgumentCountValidationMode.class); } private TestTemplateInvocationContext createInvocationContext(ParameterizedTestNameFormatter formatter, diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java index f1432328c32c..d52994bfc778 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java @@ -1117,35 +1117,52 @@ private EngineExecutionResults execute(String methodName, Class... methodPara class UnusedArgumentsWithStrictArgumentsCountIntegrationTests { @Test void failsWithArgumentsSourceProvidingUnusedArguments() { - var results = execute(UnusedArgumentsTestCase.class, "testWithTwoUnusedStringArgumentsProvider", + var results = execute(ArgumentCountValidationMode.STRICT, UnusedArgumentsTestCase.class, "testWithTwoUnusedStringArgumentsProvider", String.class); results.allEvents().assertThatEvents() // .haveExactly(1, event(EventConditions.finishedWithFailure(message( - "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided./nNote: the provided arguments are [foo, unused1]")))); + "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.\nNote: the provided arguments are [foo, unused1]")))); } @Test void failsWithMethodSourceProvidingUnusedArguments() { - var results = execute(UnusedArgumentsTestCase.class, "testWithMethodSourceProvidingUnusedArguments", + var results = execute(ArgumentCountValidationMode.STRICT, UnusedArgumentsTestCase.class, "testWithMethodSourceProvidingUnusedArguments", String.class); results.allEvents().assertThatEvents() // .haveExactly(1, event(EventConditions.finishedWithFailure(message( - "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided./nNote: the provided arguments are [foo, unused1]")))); + "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.\nNote: the provided arguments are [foo, unused1]")))); + } + + @Test + void failsWithCsvSourceUnusedArgumentsAndStrictArgumentCountValidationAnnotationAttribute() { + var results = execute(ArgumentCountValidationMode.NONE, UnusedArgumentsTestCase.class, "testWithStrictArgumentCountValidation", + String.class); + results.allEvents().assertThatEvents() // + .haveExactly(1, event(EventConditions.finishedWithFailure(message( + "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.\nNote: the provided arguments are [foo, unused1]")))); + } + + @Test + void executesWithCsvSourceUnusedArgumentsAndArgumentCountValidationAnnotationAttribute() { + var results = execute(ArgumentCountValidationMode.NONE, UnusedArgumentsTestCase.class, "testWithNoneArgumentCountValidation", String.class); + results.allEvents().assertThatEvents() // + .haveExactly(1, event(test(), displayName("[1] argument=foo"), finishedWithFailure(message("foo")))); } @Test void executesWithMethodSourceProvidingUnusedArguments() { - var results = execute(RepeatableSourcesTestCase.class, "testWithRepeatableCsvSource", String.class); + var results = execute(ArgumentCountValidationMode.STRICT, RepeatableSourcesTestCase.class, "testWithRepeatableCsvSource", String.class); results.allEvents().assertThatEvents() // .haveExactly(1, event(test(), displayName("[1] argument=a"), finishedWithFailure(message("a")))) // .haveExactly(1, event(test(), displayName("[2] argument=b"), finishedWithFailure(message("b")))); } - private EngineExecutionResults execute(Class javaClass, String methodName, + private EngineExecutionResults execute(ArgumentCountValidationMode configurationValue, Class javaClass, String methodName, Class... methodParameterTypes) { - return EngineTestKit.engine(new JupiterTestEngine()).selectors( - selectMethod(javaClass, methodName, methodParameterTypes)).configurationParameter( - ParameterizedTestExtension.ARGUMENT_COUNT_VALIDATION_KEY, "strict").execute(); + return EngineTestKit.engine(new JupiterTestEngine()) // + .selectors(selectMethod(javaClass, methodName, methodParameterTypes)) // + .configurationParameter(ParameterizedTestExtension.ARGUMENT_COUNT_VALIDATION_KEY, configurationValue.name().toLowerCase()) // + .execute(); } } @@ -2065,6 +2082,17 @@ void testWithFieldSourceProvidingUnusedArguments(String argument) { static Supplier> unusedArgumentsProviderField = // () -> Stream.of(arguments("foo", "unused1"), arguments("bar", "unused2")); + @ParameterizedTest(argumentCountValidation = ArgumentCountValidationMode.STRICT) + @CsvSource({ "foo, unused1" }) + void testWithStrictArgumentCountValidation(String argument) { + fail(argument); + } + + @ParameterizedTest(argumentCountValidation = ArgumentCountValidationMode.NONE) + @CsvSource({ "foo, unused1" }) + void testWithNoneArgumentCountValidation(String argument) { + fail(argument); + } } static class LifecycleTestCase { From 89a744385997d0098ac24b0edf560605f2ef7a68 Mon Sep 17 00:00:00 2001 From: Jonas Jebing <34072795+JonasJebing@users.noreply.github.com> Date: Wed, 23 Oct 2024 16:20:52 +0200 Subject: [PATCH 03/19] gradle spotlessApply Issue #3708 --- .../ParameterizedTestIntegrationTests.java | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java index d52994bfc778..659e8c984848 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java @@ -1117,8 +1117,8 @@ private EngineExecutionResults execute(String methodName, Class... methodPara class UnusedArgumentsWithStrictArgumentsCountIntegrationTests { @Test void failsWithArgumentsSourceProvidingUnusedArguments() { - var results = execute(ArgumentCountValidationMode.STRICT, UnusedArgumentsTestCase.class, "testWithTwoUnusedStringArgumentsProvider", - String.class); + var results = execute(ArgumentCountValidationMode.STRICT, UnusedArgumentsTestCase.class, + "testWithTwoUnusedStringArgumentsProvider", String.class); results.allEvents().assertThatEvents() // .haveExactly(1, event(EventConditions.finishedWithFailure(message( "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.\nNote: the provided arguments are [foo, unused1]")))); @@ -1126,8 +1126,8 @@ void failsWithArgumentsSourceProvidingUnusedArguments() { @Test void failsWithMethodSourceProvidingUnusedArguments() { - var results = execute(ArgumentCountValidationMode.STRICT, UnusedArgumentsTestCase.class, "testWithMethodSourceProvidingUnusedArguments", - String.class); + var results = execute(ArgumentCountValidationMode.STRICT, UnusedArgumentsTestCase.class, + "testWithMethodSourceProvidingUnusedArguments", String.class); results.allEvents().assertThatEvents() // .haveExactly(1, event(EventConditions.finishedWithFailure(message( "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.\nNote: the provided arguments are [foo, unused1]")))); @@ -1135,33 +1135,37 @@ void failsWithMethodSourceProvidingUnusedArguments() { @Test void failsWithCsvSourceUnusedArgumentsAndStrictArgumentCountValidationAnnotationAttribute() { - var results = execute(ArgumentCountValidationMode.NONE, UnusedArgumentsTestCase.class, "testWithStrictArgumentCountValidation", - String.class); + var results = execute(ArgumentCountValidationMode.NONE, UnusedArgumentsTestCase.class, + "testWithStrictArgumentCountValidation", String.class); results.allEvents().assertThatEvents() // .haveExactly(1, event(EventConditions.finishedWithFailure(message( - "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.\nNote: the provided arguments are [foo, unused1]")))); + "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.\nNote: the provided arguments are [foo, unused1]")))); } @Test void executesWithCsvSourceUnusedArgumentsAndArgumentCountValidationAnnotationAttribute() { - var results = execute(ArgumentCountValidationMode.NONE, UnusedArgumentsTestCase.class, "testWithNoneArgumentCountValidation", String.class); + var results = execute(ArgumentCountValidationMode.NONE, UnusedArgumentsTestCase.class, + "testWithNoneArgumentCountValidation", String.class); results.allEvents().assertThatEvents() // - .haveExactly(1, event(test(), displayName("[1] argument=foo"), finishedWithFailure(message("foo")))); + .haveExactly(1, + event(test(), displayName("[1] argument=foo"), finishedWithFailure(message("foo")))); } @Test void executesWithMethodSourceProvidingUnusedArguments() { - var results = execute(ArgumentCountValidationMode.STRICT, RepeatableSourcesTestCase.class, "testWithRepeatableCsvSource", String.class); + var results = execute(ArgumentCountValidationMode.STRICT, RepeatableSourcesTestCase.class, + "testWithRepeatableCsvSource", String.class); results.allEvents().assertThatEvents() // .haveExactly(1, event(test(), displayName("[1] argument=a"), finishedWithFailure(message("a")))) // .haveExactly(1, event(test(), displayName("[2] argument=b"), finishedWithFailure(message("b")))); } - private EngineExecutionResults execute(ArgumentCountValidationMode configurationValue, Class javaClass, String methodName, - Class... methodParameterTypes) { + private EngineExecutionResults execute(ArgumentCountValidationMode configurationValue, Class javaClass, + String methodName, Class... methodParameterTypes) { return EngineTestKit.engine(new JupiterTestEngine()) // .selectors(selectMethod(javaClass, methodName, methodParameterTypes)) // - .configurationParameter(ParameterizedTestExtension.ARGUMENT_COUNT_VALIDATION_KEY, configurationValue.name().toLowerCase()) // + .configurationParameter(ParameterizedTestExtension.ARGUMENT_COUNT_VALIDATION_KEY, + configurationValue.name().toLowerCase()) // .execute(); } } From 5c71632bb761ab3460280806b2510033a12e270d Mon Sep 17 00:00:00 2001 From: Jonas Jebing <34072795+JonasJebing@users.noreply.github.com> Date: Wed, 23 Oct 2024 16:37:11 +0200 Subject: [PATCH 04/19] move example to .adoc because it's a test that should error Issue #3708 --- .../src/docs/asciidoc/user-guide/writing-tests.adoc | 6 +++++- .../src/test/java/example/ParameterizedTestDemo.java | 9 --------- 2 files changed, 5 insertions(+), 10 deletions(-) diff --git a/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc b/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc index 3f408ec55cc2..90df2a481154 100644 --- a/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc +++ b/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc @@ -2041,7 +2041,11 @@ use the `argumentCountValidation` attribute of the `@ParameterizedTest` annotati [source,java,indent=0] ---- -include::{testDir}/example/ParameterizedTestDemo.java[tags=argument_count_validation] + @ParameterizedTest(argumentCountValidation = ArgumentCountValidationMode.STRICT) + @CsvSource({ "42, -666" }) + void testWithArgumentCountValidation(int number) { + assertTrue(number > 0); + } ---- [[writing-tests-parameterized-tests-argument-conversion]] diff --git a/documentation/src/test/java/example/ParameterizedTestDemo.java b/documentation/src/test/java/example/ParameterizedTestDemo.java index 3d1cc9691e67..9027b86d67e4 100644 --- a/documentation/src/test/java/example/ParameterizedTestDemo.java +++ b/documentation/src/test/java/example/ParameterizedTestDemo.java @@ -51,7 +51,6 @@ import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.parallel.Execution; -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; @@ -608,12 +607,4 @@ static Stream otherProvider() { return Stream.of("bar"); } // end::repeatable_annotations[] - - // tag::argument_count_validation[] - @ParameterizedTest(argumentCountValidation = ArgumentCountValidationMode.STRICT) - @CsvSource({ "42, -666" }) - void testWithArgumentCountValidation(int number) { - assertTrue(number > 0); - } - // end::argument_count_validation[] } From edd9f46ccd956b4a8e7ae5854d557fb6975644cf Mon Sep 17 00:00:00 2001 From: Jonas Jebing <34072795+JonasJebing@users.noreply.github.com> Date: Wed, 23 Oct 2024 16:48:36 +0200 Subject: [PATCH 05/19] fix newline char assertion for Windows Issue #3708 --- .../jupiter/params/ParameterizedTestIntegrationTests.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java index 659e8c984848..19887371ba6b 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java @@ -1121,7 +1121,7 @@ void failsWithArgumentsSourceProvidingUnusedArguments() { "testWithTwoUnusedStringArgumentsProvider", String.class); results.allEvents().assertThatEvents() // .haveExactly(1, event(EventConditions.finishedWithFailure(message( - "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.\nNote: the provided arguments are [foo, unused1]")))); + String.format("Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.%nNote: the provided arguments are [foo, unused1]"))))); } @Test @@ -1130,7 +1130,7 @@ void failsWithMethodSourceProvidingUnusedArguments() { "testWithMethodSourceProvidingUnusedArguments", String.class); results.allEvents().assertThatEvents() // .haveExactly(1, event(EventConditions.finishedWithFailure(message( - "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.\nNote: the provided arguments are [foo, unused1]")))); + String.format("Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.%nNote: the provided arguments are [foo, unused1]"))))); } @Test @@ -1139,7 +1139,7 @@ void failsWithCsvSourceUnusedArgumentsAndStrictArgumentCountValidationAnnotation "testWithStrictArgumentCountValidation", String.class); results.allEvents().assertThatEvents() // .haveExactly(1, event(EventConditions.finishedWithFailure(message( - "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.\nNote: the provided arguments are [foo, unused1]")))); + String.format("Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.%nNote: the provided arguments are [foo, unused1]"))))); } @Test From beb53ab111c6ae6c5901a0082d4fd4e017feddfe Mon Sep 17 00:00:00 2001 From: Jonas Jebing <34072795+JonasJebing@users.noreply.github.com> Date: Wed, 23 Oct 2024 17:25:54 +0200 Subject: [PATCH 06/19] gradle spotlessApply Issue #3708 --- .../params/ParameterizedTestIntegrationTests.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java index 19887371ba6b..abb0273f2136 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java @@ -1120,8 +1120,8 @@ void failsWithArgumentsSourceProvidingUnusedArguments() { var results = execute(ArgumentCountValidationMode.STRICT, UnusedArgumentsTestCase.class, "testWithTwoUnusedStringArgumentsProvider", String.class); results.allEvents().assertThatEvents() // - .haveExactly(1, event(EventConditions.finishedWithFailure(message( - String.format("Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.%nNote: the provided arguments are [foo, unused1]"))))); + .haveExactly(1, event(EventConditions.finishedWithFailure(message(String.format( + "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.%nNote: the provided arguments are [foo, unused1]"))))); } @Test @@ -1129,8 +1129,8 @@ void failsWithMethodSourceProvidingUnusedArguments() { var results = execute(ArgumentCountValidationMode.STRICT, UnusedArgumentsTestCase.class, "testWithMethodSourceProvidingUnusedArguments", String.class); results.allEvents().assertThatEvents() // - .haveExactly(1, event(EventConditions.finishedWithFailure(message( - String.format("Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.%nNote: the provided arguments are [foo, unused1]"))))); + .haveExactly(1, event(EventConditions.finishedWithFailure(message(String.format( + "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.%nNote: the provided arguments are [foo, unused1]"))))); } @Test @@ -1138,8 +1138,8 @@ void failsWithCsvSourceUnusedArgumentsAndStrictArgumentCountValidationAnnotation var results = execute(ArgumentCountValidationMode.NONE, UnusedArgumentsTestCase.class, "testWithStrictArgumentCountValidation", String.class); results.allEvents().assertThatEvents() // - .haveExactly(1, event(EventConditions.finishedWithFailure(message( - String.format("Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.%nNote: the provided arguments are [foo, unused1]"))))); + .haveExactly(1, event(EventConditions.finishedWithFailure(message(String.format( + "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.%nNote: the provided arguments are [foo, unused1]"))))); } @Test From f87404fbaae0689bdddbdfdbabefea548748250c Mon Sep 17 00:00:00 2001 From: Jonas Jebing <34072795+JonasJebing@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:15:24 +0200 Subject: [PATCH 07/19] add change to release-notes 5.12.0-M1 release-notes-5.12.0-M1-junit-jupiter-new-features-and-improvements to be exact Issue #3708 --- .../docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc index aabaedaf61eb..46ad040a7c07 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc @@ -86,6 +86,11 @@ JUnit repository on GitHub. a test-scoped `ExtensionContext` in `Extension` methods called during test class instantiation. This behavior will become the default in future versions of JUnit. * `@TempDir` is now supported on test class constructors. +* `@ParameterizedTest`s now support argument count validation as an experimental feature. + With the `junit.jupiter.params.argumentCountValidation=strict` configuration + or the `@ParameterizedTest(argumentCountValidation = ArgumentCountValidationMode.STRICT)` attribute, + any mismatch between the declared number of arguments and the number of arguments provided by the arguments source + will result in an error. By default there is only an error if there are less arguments provided than declared. [[release-notes-5.12.0-M1-junit-vintage]] From 14a071b9198db5224b3f32c8d992afb33b374ac6 Mon Sep 17 00:00:00 2001 From: JonasJebing <34072795+JonasJebing@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:17:02 +0200 Subject: [PATCH 08/19] fix ArgumentCountValidationMode javadoc typo Co-authored-by: Marc Philipp --- .../org/junit/jupiter/params/ArgumentCountValidationMode.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidationMode.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidationMode.java index 3921019637c9..be65c2278002 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidationMode.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidationMode.java @@ -27,9 +27,9 @@ @API(status = API.Status.EXPERIMENTAL, since = "5.12") public enum ArgumentCountValidationMode { /** - * Use the default cleanup mode. + * Use the default validation mode. * - *

The default cleanup mode may be changed via the + *

The default validation mode may be changed via the * {@value ParameterizedTestExtension#ARGUMENT_COUNT_VALIDATION_KEY} configuration parameter * (see the User Guide for details on configuration parameters). */ From 762c39a448d83612c5b7e7f8b308727374094e1e Mon Sep 17 00:00:00 2001 From: JonasJebing <34072795+JonasJebing@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:17:47 +0200 Subject: [PATCH 09/19] improve ParameterizedTest javadoc Co-authored-by: Marc Philipp --- .../main/java/org/junit/jupiter/params/ParameterizedTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTest.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTest.java index bb9b099f0d90..dfbd29296410 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTest.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTest.java @@ -315,7 +315,7 @@ * there might be a bug in the test method or the {@link ArgumentsSource}. * By default, the additional arguments are ignored. * {@code argumentCountValidation} allows you to control how additional arguments are handled. - * This can also be controlled via the {@value ParameterizedTestExtension#ARGUMENT_COUNT_VALIDATION_KEY} + * The default can be configured via the {@value ParameterizedTestExtension#ARGUMENT_COUNT_VALIDATION_KEY} * configuration parameter (see the User Guide for details on configuration parameters). * * @since 5.12 From 8a84dc284a10a59f29a8d7a49e629c6198cf3ecc Mon Sep 17 00:00:00 2001 From: JonasJebing <34072795+JonasJebing@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:18:11 +0200 Subject: [PATCH 10/19] use underscores for unused lambda parameter Co-authored-by: Marc Philipp --- .../org/junit/jupiter/params/ParameterizedTestExtension.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java index f558736298f1..53738467bafc 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java @@ -154,7 +154,7 @@ private ArgumentCountValidationMode getArgumentCountValidationModeConfiguration( String key = ARGUMENT_COUNT_VALIDATION_KEY; ArgumentCountValidationMode fallback = ArgumentCountValidationMode.NONE; ExtensionContext.Store store = getStoreInExtensionNamespace(extensionContext); - return store.getOrComputeIfAbsent(key, k -> { + return store.getOrComputeIfAbsent(key, __ -> { Optional optionalConfigValue = extensionContext.getConfigurationParameter(key); if (optionalConfigValue.isPresent()) { String configValue = optionalConfigValue.get(); From 09ced82b1624c497dba2fcd540746b13b1568561 Mon Sep 17 00:00:00 2001 From: JonasJebing <34072795+JonasJebing@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:18:41 +0200 Subject: [PATCH 11/19] use root context store for caching config value Co-authored-by: Marc Philipp --- .../org/junit/jupiter/params/ParameterizedTestExtension.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java index 53738467bafc..86b576999010 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java @@ -116,7 +116,7 @@ private ExtensionContext.Store getStoreInMethodNamespace(ExtensionContext contex } private ExtensionContext.Store getStoreInExtensionNamespace(ExtensionContext context) { - return context.getStore(Namespace.create(ParameterizedTestExtension.class)); + return context.getRoot().getStore(Namespace.create(ParameterizedTestExtension.class)); } private void validateArgumentCount(ExtensionContext extensionContext, Arguments arguments) { From 0bf0b05cee6da5441e0689f2ab5ace3f1dc9e88c Mon Sep 17 00:00:00 2001 From: JonasJebing <34072795+JonasJebing@users.noreply.github.com> Date: Thu, 24 Oct 2024 10:41:38 +0200 Subject: [PATCH 12/19] remove mention of experimental status from release note --- .../docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc index 46ad040a7c07..f60bfcc140c4 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc @@ -86,7 +86,7 @@ JUnit repository on GitHub. a test-scoped `ExtensionContext` in `Extension` methods called during test class instantiation. This behavior will become the default in future versions of JUnit. * `@TempDir` is now supported on test class constructors. -* `@ParameterizedTest`s now support argument count validation as an experimental feature. +* `@ParameterizedTest`s now support argument count validation. With the `junit.jupiter.params.argumentCountValidation=strict` configuration or the `@ParameterizedTest(argumentCountValidation = ArgumentCountValidationMode.STRICT)` attribute, any mismatch between the declared number of arguments and the number of arguments provided by the arguments source From 36e772910b09f9192e7c37f95cf306d01239eaf2 Mon Sep 17 00:00:00 2001 From: JonasJebing <34072795+JonasJebing@users.noreply.github.com> Date: Fri, 25 Oct 2024 15:21:38 +0200 Subject: [PATCH 13/19] `@ParameterizedTest`s to Parameterized tests Co-authored-by: Marc Philipp --- .../docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc index ac108ec2e136..5c6224049235 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc @@ -87,7 +87,7 @@ JUnit repository on GitHub. a test-scoped `ExtensionContext` in `Extension` methods called during test class instantiation. This behavior will become the default in future versions of JUnit. * `@TempDir` is now supported on test class constructors. -* `@ParameterizedTest`s now support argument count validation. +* Parameterized tests now support argument count validation. With the `junit.jupiter.params.argumentCountValidation=strict` configuration or the `@ParameterizedTest(argumentCountValidation = ArgumentCountValidationMode.STRICT)` attribute, any mismatch between the declared number of arguments and the number of arguments provided by the arguments source From 851a4db1bc95fa1c4e2bf8908c10cfc98e9033ab Mon Sep 17 00:00:00 2001 From: JonasJebing <34072795+JonasJebing@users.noreply.github.com> Date: Fri, 25 Oct 2024 15:23:09 +0200 Subject: [PATCH 14/19] improve release note Co-authored-by: Marc Philipp --- .../asciidoc/release-notes/release-notes-5.12.0-M1.adoc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc b/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc index 5c6224049235..904f2330c65d 100644 --- a/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc +++ b/documentation/src/docs/asciidoc/release-notes/release-notes-5.12.0-M1.adoc @@ -88,10 +88,11 @@ JUnit repository on GitHub. instantiation. This behavior will become the default in future versions of JUnit. * `@TempDir` is now supported on test class constructors. * Parameterized tests now support argument count validation. - With the `junit.jupiter.params.argumentCountValidation=strict` configuration - or the `@ParameterizedTest(argumentCountValidation = ArgumentCountValidationMode.STRICT)` attribute, - any mismatch between the declared number of arguments and the number of arguments provided by the arguments source - will result in an error. By default there is only an error if there are less arguments provided than declared. + If the `junit.jupiter.params.argumentCountValidation=strict` configuration parameter + or the `@ParameterizedTest(argumentCountValidation = STRICT)` attribute is set, any + mismatch between the declared number of arguments and the number of arguments provided + by the arguments source will result in an error. By default, it's still only an error if + there are fewer arguments provided than declared. [[release-notes-5.12.0-M1-junit-vintage]] From a986bba97bfbf0feb343b79946f22d06e1df4dba Mon Sep 17 00:00:00 2001 From: Jonas Jebing <34072795+JonasJebing@users.noreply.github.com> Date: Fri, 25 Oct 2024 15:30:30 +0200 Subject: [PATCH 15/19] move user guide example to ParameterizedTestDemo Issue #3708 --- .../src/docs/asciidoc/user-guide/writing-tests.adoc | 6 +----- .../src/test/java/example/ParameterizedTestDemo.java | 10 ++++++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc b/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc index 90df2a481154..3f408ec55cc2 100644 --- a/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc +++ b/documentation/src/docs/asciidoc/user-guide/writing-tests.adoc @@ -2041,11 +2041,7 @@ use the `argumentCountValidation` attribute of the `@ParameterizedTest` annotati [source,java,indent=0] ---- - @ParameterizedTest(argumentCountValidation = ArgumentCountValidationMode.STRICT) - @CsvSource({ "42, -666" }) - void testWithArgumentCountValidation(int number) { - assertTrue(number > 0); - } +include::{testDir}/example/ParameterizedTestDemo.java[tags=argument_count_validation] ---- [[writing-tests-parameterized-tests-argument-conversion]] diff --git a/documentation/src/test/java/example/ParameterizedTestDemo.java b/documentation/src/test/java/example/ParameterizedTestDemo.java index 9027b86d67e4..894b7617761d 100644 --- a/documentation/src/test/java/example/ParameterizedTestDemo.java +++ b/documentation/src/test/java/example/ParameterizedTestDemo.java @@ -51,6 +51,7 @@ import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.api.parallel.Execution; +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; @@ -607,4 +608,13 @@ static Stream otherProvider() { return Stream.of("bar"); } // end::repeatable_annotations[] + + @extensions.ExpectToFail + // tag::argument_count_validation[] + @ParameterizedTest(argumentCountValidation = ArgumentCountValidationMode.STRICT) + @CsvSource({ "42, -666" }) + void testWithArgumentCountValidation(int number) { + assertTrue(number > 0); + } + // end::argument_count_validation[] } From e70240c0b4df17ababa89f3f25d56bbae1335322 Mon Sep 17 00:00:00 2001 From: Jonas Jebing <34072795+JonasJebing@users.noreply.github.com> Date: Tue, 12 Nov 2024 17:15:13 +0100 Subject: [PATCH 16/19] move argument count validation to happen later This should result in invocations with valid argument counts still being executed and only invocations with invalid argument counts fail. Issue #3708 --- .../params/ArgumentCountValidator.java | 112 ++++++++++++++++++ .../params/ParameterizedTestExtension.java | 81 +------------ .../ParameterizedTestInvocationContext.java | 7 +- .../ParameterizedTestIntegrationTests.java | 19 ++- 4 files changed, 136 insertions(+), 83 deletions(-) create mode 100644 junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidator.java diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidator.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidator.java new file mode 100644 index 000000000000..8499aaed7dce --- /dev/null +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidator.java @@ -0,0 +1,112 @@ +/* + * Copyright 2015-2024 the original author or authors. + * + * All rights reserved. This program and the accompanying materials are + * made available under the terms of the Eclipse Public License v2.0 which + * accompanies this distribution and is available at + * + * https://www.eclipse.org/legal/epl-v20.html + */ + +package org.junit.jupiter.params; + +import static org.junit.platform.commons.support.AnnotationSupport.findAnnotation; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.NoSuchElementException; +import java.util.Optional; + +import org.junit.jupiter.api.extension.ExtensionConfigurationException; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.InvocationInterceptor; +import org.junit.jupiter.api.extension.ReflectiveInvocationContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.platform.commons.logging.Logger; +import org.junit.platform.commons.logging.LoggerFactory; +import org.junit.platform.commons.util.Preconditions; + +class ArgumentCountValidator implements InvocationInterceptor { + private static final Logger logger = LoggerFactory.getLogger(ArgumentCountValidator.class); + + static final String ARGUMENT_COUNT_VALIDATION_KEY = "junit.jupiter.params.argumentCountValidation"; + + private final Arguments arguments; + + ArgumentCountValidator(Arguments arguments) { + this.arguments = arguments; + } + + @Override + public void interceptTestTemplateMethod(InvocationInterceptor.Invocation invocation, + ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable { + validateArgumentCount(extensionContext, arguments); + invocation.proceed(); + } + + private ExtensionContext.Store getStore(ExtensionContext context) { + return context.getRoot().getStore(ExtensionContext.Namespace.create(getClass())); + } + + private void validateArgumentCount(ExtensionContext extensionContext, Arguments arguments) { + ArgumentCountValidationMode argumentCountValidationMode = getArgumentCountValidationMode(extensionContext); + switch (argumentCountValidationMode) { + case DEFAULT: + case NONE: + return; + case STRICT: + int testParamCount = extensionContext.getRequiredTestMethod().getParameterCount(); + int argumentsCount = arguments.get().length; + Preconditions.condition(testParamCount == argumentsCount, () -> String.format( + "Configuration error: the @ParameterizedTest has %s argument(s) but there were %s argument(s) provided.%nNote: the provided arguments are %s", + testParamCount, argumentsCount, Arrays.toString(arguments.get()))); + break; + default: + throw new ExtensionConfigurationException( + "Unsupported argument count validation mode: " + argumentCountValidationMode); + } + } + + private ArgumentCountValidationMode getArgumentCountValidationMode(ExtensionContext extensionContext) { + ParameterizedTest parameterizedTest = findAnnotation(// + extensionContext.getRequiredTestMethod(), ParameterizedTest.class// + ).orElseThrow(NoSuchElementException::new); + if (parameterizedTest.argumentCountValidation() != ArgumentCountValidationMode.DEFAULT) { + return parameterizedTest.argumentCountValidation(); + } + else { + return getArgumentCountValidationModeConfiguration(extensionContext); + } + } + + private ArgumentCountValidationMode getArgumentCountValidationModeConfiguration(ExtensionContext extensionContext) { + String key = ARGUMENT_COUNT_VALIDATION_KEY; + ArgumentCountValidationMode fallback = ArgumentCountValidationMode.NONE; + ExtensionContext.Store store = getStore(extensionContext); + return store.getOrComputeIfAbsent(key, __ -> { + Optional optionalConfigValue = extensionContext.getConfigurationParameter(key); + if (optionalConfigValue.isPresent()) { + String configValue = optionalConfigValue.get(); + Optional enumValue = Arrays.stream( + ArgumentCountValidationMode.values()).filter( + mode -> mode.name().equalsIgnoreCase(configValue)).findFirst(); + if (enumValue.isPresent()) { + logger.config(() -> String.format( + "Using ArgumentCountValidationMode '%s' set via the '%s' configuration parameter.", + enumValue.get().name(), key)); + return enumValue.get(); + } + else { + logger.warn(() -> String.format( + "Invalid ArgumentCountValidationMode '%s' set via the '%s' configuration parameter. " + + "Falling back to the %s default value.", + configValue, key, fallback.name())); + return fallback; + } + } + else { + return fallback; + } + }, ArgumentCountValidationMode.class); + } +} diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java index 86b576999010..74ff653e15cc 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestExtension.java @@ -14,13 +14,10 @@ import static org.junit.platform.commons.support.AnnotationSupport.findRepeatableAnnotations; import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.NoSuchElementException; import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Stream; -import org.junit.jupiter.api.extension.ExtensionConfigurationException; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext.Namespace; import org.junit.jupiter.api.extension.TestTemplateInvocationContext; @@ -29,8 +26,6 @@ import org.junit.jupiter.params.provider.ArgumentsProvider; import org.junit.jupiter.params.provider.ArgumentsSource; import org.junit.jupiter.params.support.AnnotationConsumerInitializer; -import org.junit.platform.commons.logging.Logger; -import org.junit.platform.commons.logging.LoggerFactory; import org.junit.platform.commons.util.ExceptionUtils; import org.junit.platform.commons.util.Preconditions; @@ -39,13 +34,10 @@ */ class ParameterizedTestExtension implements TestTemplateInvocationContextProvider { - private static final Logger logger = LoggerFactory.getLogger(ParameterizedTestExtension.class); - static final String METHOD_CONTEXT_KEY = "context"; static final String ARGUMENT_MAX_LENGTH_KEY = "junit.jupiter.params.displayname.argument.maxlength"; static final String DEFAULT_DISPLAY_NAME = "{default_display_name}"; static final String DISPLAY_NAME_PATTERN_KEY = "junit.jupiter.params.displayname.default"; - static final String ARGUMENT_COUNT_VALIDATION_KEY = "junit.jupiter.params.argumentCountValidation"; @Override public boolean supportsTestTemplate(ExtensionContext context) { @@ -69,7 +61,7 @@ public boolean supportsTestTemplate(ExtensionContext context) { + "and before any arguments resolved by another ParameterResolver.", templateMethod.toGenericString())); - getStoreInMethodNamespace(context).put(METHOD_CONTEXT_KEY, methodContext); + getStore(context).put(METHOD_CONTEXT_KEY, methodContext); return true; } @@ -90,7 +82,6 @@ public Stream provideTestTemplateInvocationContex .map(provider -> AnnotationConsumerInitializer.initialize(methodContext.method, provider)) .flatMap(provider -> arguments(provider, extensionContext)) .map(arguments -> { - validateArgumentCount(extensionContext, arguments); invocationCount.incrementAndGet(); return createInvocationContext(formatter, methodContext, arguments, invocationCount.intValue()); }) @@ -107,80 +98,14 @@ public boolean mayReturnZeroTestTemplateInvocationContexts(ExtensionContext exte } private ParameterizedTestMethodContext getMethodContext(ExtensionContext extensionContext) { - return getStoreInMethodNamespace(extensionContext)// + return getStore(extensionContext)// .get(METHOD_CONTEXT_KEY, ParameterizedTestMethodContext.class); } - private ExtensionContext.Store getStoreInMethodNamespace(ExtensionContext context) { + private ExtensionContext.Store getStore(ExtensionContext context) { return context.getStore(Namespace.create(ParameterizedTestExtension.class, context.getRequiredTestMethod())); } - private ExtensionContext.Store getStoreInExtensionNamespace(ExtensionContext context) { - return context.getRoot().getStore(Namespace.create(ParameterizedTestExtension.class)); - } - - private void validateArgumentCount(ExtensionContext extensionContext, Arguments arguments) { - ArgumentCountValidationMode argumentCountValidationMode = getArgumentCountValidationMode(extensionContext); - switch (argumentCountValidationMode) { - case DEFAULT: - case NONE: - return; - case STRICT: - int testParamCount = extensionContext.getRequiredTestMethod().getParameterCount(); - int argumentsCount = arguments.get().length; - Preconditions.condition(testParamCount == argumentsCount, () -> String.format( - "Configuration error: the @ParameterizedTest has %s argument(s) but there were %s argument(s) provided.%nNote: the provided arguments are %s", - testParamCount, argumentsCount, Arrays.toString(arguments.get()))); - break; - default: - throw new ExtensionConfigurationException( - "Unsupported argument count validation mode: " + argumentCountValidationMode); - } - } - - private ArgumentCountValidationMode getArgumentCountValidationMode(ExtensionContext extensionContext) { - ParameterizedTest parameterizedTest = findAnnotation(// - extensionContext.getRequiredTestMethod(), ParameterizedTest.class// - ).orElseThrow(NoSuchElementException::new); - if (parameterizedTest.argumentCountValidation() != ArgumentCountValidationMode.DEFAULT) { - return parameterizedTest.argumentCountValidation(); - } - else { - return getArgumentCountValidationModeConfiguration(extensionContext); - } - } - - private ArgumentCountValidationMode getArgumentCountValidationModeConfiguration(ExtensionContext extensionContext) { - String key = ARGUMENT_COUNT_VALIDATION_KEY; - ArgumentCountValidationMode fallback = ArgumentCountValidationMode.NONE; - ExtensionContext.Store store = getStoreInExtensionNamespace(extensionContext); - return store.getOrComputeIfAbsent(key, __ -> { - Optional optionalConfigValue = extensionContext.getConfigurationParameter(key); - if (optionalConfigValue.isPresent()) { - String configValue = optionalConfigValue.get(); - Optional enumValue = Arrays.stream( - ArgumentCountValidationMode.values()).filter( - mode -> mode.name().equalsIgnoreCase(configValue)).findFirst(); - if (enumValue.isPresent()) { - logger.config(() -> String.format( - "Using ArgumentCountValidationMode '%s' set via the '%s' configuration parameter.", - enumValue.get().name(), key)); - return enumValue.get(); - } - else { - logger.warn(() -> String.format( - "Invalid ArgumentCountValidationMode '%s' set via the '%s' configuration parameter. " - + "Falling back to the %s default value.", - configValue, key, fallback.name())); - return fallback; - } - } - else { - return fallback; - } - }, ArgumentCountValidationMode.class); - } - private TestTemplateInvocationContext createInvocationContext(ParameterizedTestNameFormatter formatter, ParameterizedTestMethodContext methodContext, Arguments arguments, int invocationIndex) { diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestInvocationContext.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestInvocationContext.java index c0ac83e78717..18d7784bbad3 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestInvocationContext.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestInvocationContext.java @@ -10,8 +10,6 @@ package org.junit.jupiter.params; -import static java.util.Collections.singletonList; - import java.util.Arrays; import java.util.List; @@ -47,8 +45,9 @@ public String getDisplayName(int invocationIndex) { @Override public List getAdditionalExtensions() { - return singletonList( - new ParameterizedTestParameterResolver(this.methodContext, this.consumedArguments, this.invocationIndex)); + return Arrays.asList( + new ParameterizedTestParameterResolver(this.methodContext, this.consumedArguments, this.invocationIndex), + new ArgumentCountValidator(this.arguments)); } private static Object[] consumedArguments(ParameterizedTestMethodContext methodContext, Object[] arguments) { diff --git a/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java index abb0273f2136..50c131c449b3 100644 --- a/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java +++ b/jupiter-tests/src/test/java/org/junit/jupiter/params/ParameterizedTestIntegrationTests.java @@ -1142,6 +1142,17 @@ void failsWithCsvSourceUnusedArgumentsAndStrictArgumentCountValidationAnnotation "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.%nNote: the provided arguments are [foo, unused1]"))))); } + @Test + void failsWithCsvSourceUnusedArgumentsButExecutesRemainingArgumentsWhereThereIsNoUnusedArgument() { + var results = execute(ArgumentCountValidationMode.STRICT, UnusedArgumentsTestCase.class, + "testWithCsvSourceContainingDifferentNumbersOfArguments", String.class); + results.allEvents().assertThatEvents() // + .haveExactly(1, event(EventConditions.finishedWithFailure(message(String.format( + "Configuration error: the @ParameterizedTest has 1 argument(s) but there were 2 argument(s) provided.%nNote: the provided arguments are [foo, unused1]"))))) // + .haveExactly(1, + event(test(), displayName("[2] argument=bar"), finishedWithFailure(message("bar")))); + } + @Test void executesWithCsvSourceUnusedArgumentsAndArgumentCountValidationAnnotationAttribute() { var results = execute(ArgumentCountValidationMode.NONE, UnusedArgumentsTestCase.class, @@ -1164,7 +1175,7 @@ private EngineExecutionResults execute(ArgumentCountValidationMode configuration String methodName, Class... methodParameterTypes) { return EngineTestKit.engine(new JupiterTestEngine()) // .selectors(selectMethod(javaClass, methodName, methodParameterTypes)) // - .configurationParameter(ParameterizedTestExtension.ARGUMENT_COUNT_VALIDATION_KEY, + .configurationParameter(ArgumentCountValidator.ARGUMENT_COUNT_VALIDATION_KEY, configurationValue.name().toLowerCase()) // .execute(); } @@ -2097,6 +2108,12 @@ void testWithStrictArgumentCountValidation(String argument) { void testWithNoneArgumentCountValidation(String argument) { fail(argument); } + + @ParameterizedTest + @CsvSource({ "foo, unused1", "bar" }) + void testWithCsvSourceContainingDifferentNumbersOfArguments(String argument) { + fail(argument); + } } static class LifecycleTestCase { From 3d84b9f842387b88a39256d8738bec42915b87de Mon Sep 17 00:00:00 2001 From: Jonas Jebing <34072795+JonasJebing@users.noreply.github.com> Date: Tue, 12 Nov 2024 17:31:22 +0100 Subject: [PATCH 17/19] update javadoc to use new ArgumentCountValidator Issue #3708 --- .../org/junit/jupiter/params/ArgumentCountValidationMode.java | 2 +- .../main/java/org/junit/jupiter/params/ParameterizedTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidationMode.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidationMode.java index be65c2278002..38c9beb8982a 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidationMode.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidationMode.java @@ -30,7 +30,7 @@ public enum ArgumentCountValidationMode { * Use the default validation mode. * *

The default validation mode may be changed via the - * {@value ParameterizedTestExtension#ARGUMENT_COUNT_VALIDATION_KEY} configuration parameter + * {@value ArgumentCountValidator#ARGUMENT_COUNT_VALIDATION_KEY} configuration parameter * (see the User Guide for details on configuration parameters). */ DEFAULT, diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTest.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTest.java index dfbd29296410..03741d19f1fd 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTest.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTest.java @@ -315,7 +315,7 @@ * there might be a bug in the test method or the {@link ArgumentsSource}. * By default, the additional arguments are ignored. * {@code argumentCountValidation} allows you to control how additional arguments are handled. - * The default can be configured via the {@value ParameterizedTestExtension#ARGUMENT_COUNT_VALIDATION_KEY} + * The default can be configured via the {@value ArgumentCountValidator#ARGUMENT_COUNT_VALIDATION_KEY} * configuration parameter (see the User Guide for details on configuration parameters). * * @since 5.12 From dd6b55adc7ab5b88b050253ea4366a580e3f6805 Mon Sep 17 00:00:00 2001 From: Jonas Jebing <34072795+JonasJebing@users.noreply.github.com> Date: Wed, 13 Nov 2024 09:41:48 +0100 Subject: [PATCH 18/19] retrigger checks From 906881ce774836fb64f540782be3edc09aef113b Mon Sep 17 00:00:00 2001 From: Jonas Jebing <34072795+JonasJebing@users.noreply.github.com> Date: Thu, 14 Nov 2024 12:54:40 +0100 Subject: [PATCH 19/19] add small ArgumentCountValidator perf optimisations Issue #3708 --- .../jupiter/params/ArgumentCountValidator.java | 15 +++++++-------- .../ParameterizedTestInvocationContext.java | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidator.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidator.java index 8499aaed7dce..220825d9817a 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidator.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ArgumentCountValidator.java @@ -10,11 +10,8 @@ package org.junit.jupiter.params; -import static org.junit.platform.commons.support.AnnotationSupport.findAnnotation; - import java.lang.reflect.Method; import java.util.Arrays; -import java.util.NoSuchElementException; import java.util.Optional; import org.junit.jupiter.api.extension.ExtensionConfigurationException; @@ -30,10 +27,14 @@ class ArgumentCountValidator implements InvocationInterceptor { private static final Logger logger = LoggerFactory.getLogger(ArgumentCountValidator.class); static final String ARGUMENT_COUNT_VALIDATION_KEY = "junit.jupiter.params.argumentCountValidation"; + private static final ExtensionContext.Namespace NAMESPACE = ExtensionContext.Namespace.create( + ArgumentCountValidator.class); + private final ParameterizedTestMethodContext methodContext; private final Arguments arguments; - ArgumentCountValidator(Arguments arguments) { + ArgumentCountValidator(ParameterizedTestMethodContext methodContext, Arguments arguments) { + this.methodContext = methodContext; this.arguments = arguments; } @@ -45,7 +46,7 @@ public void interceptTestTemplateMethod(InvocationInterceptor.Invocation i } private ExtensionContext.Store getStore(ExtensionContext context) { - return context.getRoot().getStore(ExtensionContext.Namespace.create(getClass())); + return context.getRoot().getStore(NAMESPACE); } private void validateArgumentCount(ExtensionContext extensionContext, Arguments arguments) { @@ -68,9 +69,7 @@ private void validateArgumentCount(ExtensionContext extensionContext, Arguments } private ArgumentCountValidationMode getArgumentCountValidationMode(ExtensionContext extensionContext) { - ParameterizedTest parameterizedTest = findAnnotation(// - extensionContext.getRequiredTestMethod(), ParameterizedTest.class// - ).orElseThrow(NoSuchElementException::new); + ParameterizedTest parameterizedTest = methodContext.annotation; if (parameterizedTest.argumentCountValidation() != ArgumentCountValidationMode.DEFAULT) { return parameterizedTest.argumentCountValidation(); } diff --git a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestInvocationContext.java b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestInvocationContext.java index 18d7784bbad3..ab26b362c362 100644 --- a/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestInvocationContext.java +++ b/junit-jupiter-params/src/main/java/org/junit/jupiter/params/ParameterizedTestInvocationContext.java @@ -47,7 +47,7 @@ public String getDisplayName(int invocationIndex) { public List getAdditionalExtensions() { return Arrays.asList( new ParameterizedTestParameterResolver(this.methodContext, this.consumedArguments, this.invocationIndex), - new ArgumentCountValidator(this.arguments)); + new ArgumentCountValidator(this.methodContext, this.arguments)); } private static Object[] consumedArguments(ParameterizedTestMethodContext methodContext, Object[] arguments) {