1515import static org .junit .platform .commons .support .AnnotationSupport .isAnnotated ;
1616
1717import java .lang .reflect .Method ;
18+ import java .util .Arrays ;
19+ import java .util .NoSuchElementException ;
20+ import java .util .Optional ;
1821import java .util .concurrent .atomic .AtomicLong ;
1922import java .util .stream .Stream ;
2023
24+ import org .junit .jupiter .api .extension .ExtensionConfigurationException ;
2125import org .junit .jupiter .api .extension .ExtensionContext ;
2226import org .junit .jupiter .api .extension .ExtensionContext .Namespace ;
2327import org .junit .jupiter .api .extension .TestTemplateInvocationContext ;
2630import org .junit .jupiter .params .provider .ArgumentsProvider ;
2731import org .junit .jupiter .params .provider .ArgumentsSource ;
2832import org .junit .jupiter .params .support .AnnotationConsumerInitializer ;
33+ import org .junit .platform .commons .logging .Logger ;
34+ import org .junit .platform .commons .logging .LoggerFactory ;
2935import org .junit .platform .commons .util .ExceptionUtils ;
3036import org .junit .platform .commons .util .Preconditions ;
3137
3440 */
3541class ParameterizedTestExtension implements TestTemplateInvocationContextProvider {
3642
43+ private static final Logger logger = LoggerFactory .getLogger (ParameterizedTestExtension .class );
44+
3745 private static final String METHOD_CONTEXT_KEY = "context" ;
3846 static final String ARGUMENT_MAX_LENGTH_KEY = "junit.jupiter.params.displayname.argument.maxlength" ;
3947 static final String DEFAULT_DISPLAY_NAME = "{default_display_name}" ;
4048 static final String DISPLAY_NAME_PATTERN_KEY = "junit.jupiter.params.displayname.default" ;
49+ static final String ARGUMENT_COUNT_VALIDATION_KEY = "junit.jupiter.params.argumentCountValidation" ;
4150
4251 @ Override
4352 public boolean supportsTestTemplate (ExtensionContext context ) {
@@ -86,6 +95,7 @@ public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContex
8695 .map (provider -> AnnotationConsumerInitializer .initialize (templateMethod , provider ))
8796 .flatMap (provider -> arguments (provider , extensionContext ))
8897 .map (arguments -> {
98+ validateArgumentCount (extensionContext , arguments );
8999 invocationCount .incrementAndGet ();
90100 return createInvocationContext (formatter , methodContext , arguments , invocationCount .intValue ());
91101 })
@@ -99,6 +109,55 @@ private ExtensionContext.Store getStore(ExtensionContext context) {
99109 return context .getStore (Namespace .create (ParameterizedTestExtension .class , context .getRequiredTestMethod ()));
100110 }
101111
112+ private void validateArgumentCount (ExtensionContext extensionContext , Arguments arguments ) {
113+ ArgumentCountValidationMode argumentCountValidationMode = getArgumentCountValidationMode (extensionContext );
114+ switch (argumentCountValidationMode ) {
115+ case DEFAULT :
116+ case NONE :
117+ return ;
118+ case STRICT :
119+ int testParamCount = extensionContext .getRequiredTestMethod ().getParameterCount ();
120+ int argumentsCount = arguments .get ().length ;
121+ Preconditions .condition (testParamCount == argumentsCount , () -> String .format (
122+ "Configuration error: the @ParameterizedTest has %s argument(s) but there were %s argument(s) provided./nNote: the provided arguments are %s" ,
123+ testParamCount , argumentsCount , Arrays .toString (arguments .get ())));
124+ break ;
125+ default :
126+ throw new ExtensionConfigurationException (
127+ "Unsupported argument count validation mode: " + argumentCountValidationMode );
128+ }
129+ }
130+
131+ private ArgumentCountValidationMode getArgumentCountValidationMode (ExtensionContext extensionContext ) {
132+ ParameterizedTest parameterizedTest = findAnnotation (//
133+ extensionContext .getRequiredTestMethod (), ParameterizedTest .class //
134+ ).orElseThrow (NoSuchElementException ::new );
135+ if (parameterizedTest .argumentCountValidation () != ArgumentCountValidationMode .DEFAULT ) {
136+ return parameterizedTest .argumentCountValidation ();
137+ }
138+ else {
139+ return getArgumentCountValidationModeConfiguration (extensionContext );
140+ }
141+ }
142+
143+ private ArgumentCountValidationMode getArgumentCountValidationModeConfiguration (ExtensionContext extensionContext ) {
144+ String key = ARGUMENT_COUNT_VALIDATION_KEY ;
145+ ArgumentCountValidationMode fallback = ArgumentCountValidationMode .DEFAULT ;
146+ Optional <String > optionalValue = extensionContext .getConfigurationParameter (key );
147+ if (optionalValue .isPresent ()) {
148+ String value = optionalValue .get ();
149+ return Arrays .stream (ArgumentCountValidationMode .values ()).filter (
150+ mode -> mode .name ().equalsIgnoreCase (value )).findFirst ().orElseGet (() -> {
151+ logger .warn (() -> String .format (
152+ "Ignored invalid configuration '%s' set via the '%s' configuration parameter." , value , key ));
153+ return fallback ;
154+ });
155+ }
156+ else {
157+ return fallback ;
158+ }
159+ }
160+
102161 private TestTemplateInvocationContext createInvocationContext (ParameterizedTestNameFormatter formatter ,
103162 ParameterizedTestMethodContext methodContext , Arguments arguments , int invocationIndex ) {
104163
0 commit comments