Skip to content

Commit 6882b3d

Browse files
Closes #6277
1 parent ec1f77f commit 6882b3d

11 files changed

+136
-33
lines changed

ChangeLog-12.4.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,8 @@ All notable changes of the PHPUnit 12.4 release series are documented in this fi
44

55
## [12.4.0] - 2025-10-03
66

7+
### Added
8+
9+
* [#6277](https://github.com/sebastianbergmann/phpunit/issues/6277): Allow tests to opt out of the validation that a data provider method does not provide data sets with more values than a test method accepts
10+
711
[12.4.0]: https://github.com/sebastianbergmann/phpunit/compare/12.3...main

src/Framework/Attributes/DataProvider.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@
2323
* @var non-empty-string
2424
*/
2525
private string $methodName;
26+
private bool $validateArgumentCount;
2627

2728
/**
2829
* @param non-empty-string $methodName
2930
*/
30-
public function __construct(string $methodName)
31+
public function __construct(string $methodName, bool $validateArgumentCount = true)
3132
{
32-
$this->methodName = $methodName;
33+
$this->methodName = $methodName;
34+
$this->validateArgumentCount = $validateArgumentCount;
3335
}
3436

3537
/**
@@ -39,4 +41,9 @@ public function methodName(): string
3941
{
4042
return $this->methodName;
4143
}
44+
45+
public function validateArgumentCount(): bool
46+
{
47+
return $this->validateArgumentCount;
48+
}
4249
}

src/Framework/Attributes/DataProviderExternal.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,17 @@
2828
* @var non-empty-string
2929
*/
3030
private string $methodName;
31+
private bool $validateArgumentCount;
3132

3233
/**
3334
* @param class-string $className
3435
* @param non-empty-string $methodName
3536
*/
36-
public function __construct(string $className, string $methodName)
37+
public function __construct(string $className, string $methodName, bool $validateArgumentCount = true)
3738
{
38-
$this->className = $className;
39-
$this->methodName = $methodName;
39+
$this->className = $className;
40+
$this->methodName = $methodName;
41+
$this->validateArgumentCount = $validateArgumentCount;
4042
}
4143

4244
/**
@@ -54,4 +56,9 @@ public function methodName(): string
5456
{
5557
return $this->methodName;
5658
}
59+
60+
public function validateArgumentCount(): bool
61+
{
62+
return $this->validateArgumentCount;
63+
}
5764
}

src/Metadata/Api/DataProvider.php

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -81,14 +81,17 @@ private function dataProvidedByMethods(ReflectionMethod $testMethod, MetadataCol
8181
$testMethod->getName(),
8282
);
8383

84-
$methodsCalled = [];
85-
$result = [];
84+
$methodsCalled = [];
85+
$result = [];
86+
$testMethodNumberOfParameters = $testMethod->getNumberOfParameters();
87+
$testMethodIsNonVariadic = !$testMethod->isVariadic();
8688

8789
foreach ($dataProvider as $_dataProvider) {
8890
assert($_dataProvider instanceof DataProviderMetadata);
8991

90-
$providerLabel = $_dataProvider->className() . '::' . $_dataProvider->methodName();
91-
$dataProviderMethod = new Event\Code\ClassMethod($_dataProvider->className(), $_dataProvider->methodName());
92+
$providerLabel = $_dataProvider->className() . '::' . $_dataProvider->methodName();
93+
$dataProviderMethod = new Event\Code\ClassMethod($_dataProvider->className(), $_dataProvider->methodName());
94+
$validateArgumentCount = $testMethodIsNonVariadic && $_dataProvider->validateArgumentCount();
9295

9396
Event\Facade::emitter()->dataProviderMethodCalled(
9497
$testMethodValueObject,
@@ -168,6 +171,32 @@ private function dataProvidedByMethods(ReflectionMethod $testMethod, MetadataCol
168171
);
169172
}
170173

174+
if (!is_array($value)) {
175+
Event\Facade::emitter()->dataProviderMethodFinished(
176+
$testMethodValueObject,
177+
...$methodsCalled,
178+
);
179+
180+
throw new InvalidDataProviderException(
181+
sprintf(
182+
'Data set %s provided by %s is invalid, expected array but got %s',
183+
$this->formatKey($key),
184+
$providerLabel,
185+
get_debug_type($value),
186+
),
187+
);
188+
}
189+
190+
if ($validateArgumentCount && $testMethodNumberOfParameters < count($value)) {
191+
$this->triggerWarningForArgumentCount(
192+
$testMethod,
193+
$this->formatKey($key),
194+
$providerLabel,
195+
count($value),
196+
$testMethodNumberOfParameters,
197+
);
198+
}
199+
171200
if (is_int($key)) {
172201
$result[] = new ProvidedData($providerLabel, $value);
173202

@@ -204,8 +233,6 @@ private function dataProvidedByMethods(ReflectionMethod $testMethod, MetadataCol
204233
);
205234
}
206235

207-
$this->validate($testMethod, $result);
208-
209236
return $result;
210237
}
211238

@@ -240,22 +267,10 @@ private function dataProvidedByMetadata(ReflectionMethod $testMethod, MetadataCo
240267
}
241268
}
242269

243-
$this->validate($testMethod, $result);
244-
245-
return $result;
246-
}
247-
248-
/**
249-
* @param array<ProvidedData> $data
250-
*
251-
* @throws InvalidDataProviderException
252-
*/
253-
private function validate(ReflectionMethod $testMethod, array $data): void
254-
{
255270
$testMethodNumberOfParameters = $testMethod->getNumberOfParameters();
256271
$testMethodIsNonVariadic = !$testMethod->isVariadic();
257272

258-
foreach ($data as $key => $providedData) {
273+
foreach ($result as $key => $providedData) {
259274
$value = $providedData->value();
260275

261276
if (!is_array($value)) {
@@ -279,6 +294,8 @@ private function validate(ReflectionMethod $testMethod, array $data): void
279294
);
280295
}
281296
}
297+
298+
return $result;
282299
}
283300

284301
/**

src/Metadata/DataProvider.php

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,20 @@
2525
* @var non-empty-string
2626
*/
2727
private string $methodName;
28+
private bool $validateArgumentCount;
2829

2930
/**
3031
* @param int<0, 1> $level
3132
* @param class-string $className
3233
* @param non-empty-string $methodName
3334
*/
34-
protected function __construct(int $level, string $className, string $methodName)
35+
protected function __construct(int $level, string $className, string $methodName, bool $validateArgumentCount)
3536
{
3637
parent::__construct($level);
3738

38-
$this->className = $className;
39-
$this->methodName = $methodName;
39+
$this->className = $className;
40+
$this->methodName = $methodName;
41+
$this->validateArgumentCount = $validateArgumentCount;
4042
}
4143

4244
public function isDataProvider(): true
@@ -59,4 +61,9 @@ public function methodName(): string
5961
{
6062
return $this->methodName;
6163
}
64+
65+
public function validateArgumentCount(): bool
66+
{
67+
return $this->validateArgumentCount;
68+
}
6269
}

src/Metadata/Metadata.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,9 @@ public static function coversNothingOnMethod(): CoversNothing
138138
* @param class-string $className
139139
* @param non-empty-string $methodName
140140
*/
141-
public static function dataProvider(string $className, string $methodName): DataProvider
141+
public static function dataProvider(string $className, string $methodName, bool $validateArgumentCount): DataProvider
142142
{
143-
return new DataProvider(self::METHOD_LEVEL, $className, $methodName);
143+
return new DataProvider(self::METHOD_LEVEL, $className, $methodName, $validateArgumentCount);
144144
}
145145

146146
/**

src/Metadata/Parser/AttributeParser.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -588,14 +588,14 @@ public function forMethod(string $className, string $methodName): MetadataCollec
588588
case DataProvider::class:
589589
assert($attributeInstance instanceof DataProvider);
590590

591-
$result[] = Metadata::dataProvider($className, $attributeInstance->methodName());
591+
$result[] = Metadata::dataProvider($className, $attributeInstance->methodName(), $attributeInstance->validateArgumentCount());
592592

593593
break;
594594

595595
case DataProviderExternal::class:
596596
assert($attributeInstance instanceof DataProviderExternal);
597597

598-
$result[] = Metadata::dataProvider($attributeInstance->className(), $attributeInstance->methodName());
598+
$result[] = Metadata::dataProvider($attributeInstance->className(), $attributeInstance->methodName(), $attributeInstance->validateArgumentCount());
599599

600600
break;
601601

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* This file is part of PHPUnit.
4+
*
5+
* (c) Sebastian Bergmann <[email protected]>
6+
*
7+
* For the full copyright and license information, please view the LICENSE
8+
* file that was distributed with this source code.
9+
*/
10+
namespace PHPUnit\TestFixture;
11+
12+
use PHPUnit\Framework\Attributes\DataProvider;
13+
use PHPUnit\Framework\TestCase;
14+
15+
final class DataProviderTooManyArgumentsNoValidationTest extends TestCase
16+
{
17+
public static function provider(): iterable
18+
{
19+
// correct case, 2nd parameter is not required
20+
yield [true];
21+
22+
// correct case
23+
yield [true, true];
24+
25+
// incorrect case
26+
yield [true, true, 'Third argument, but test method only has two.'];
27+
}
28+
29+
#[DataProvider('provider', false)]
30+
public function testMethodHavingTwoParameters(bool $x1, bool $x2 = true): void
31+
{
32+
$this->assertSame($x1, $x2);
33+
}
34+
35+
#[DataProvider('provider')]
36+
public function testMethodHavingVariadicParameter(bool $x1, ...$rest): void
37+
{
38+
$this->assertTrue($x1);
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
--TEST--
2+
phpunit ../../_files/DataProviderTooManyArgumentsTest.php
3+
--FILE--
4+
<?php declare(strict_types=1);
5+
$_SERVER['argv'][] = '--do-not-cache-result';
6+
$_SERVER['argv'][] = '--no-configuration';
7+
$_SERVER['argv'][] = __DIR__ . '/../../_files/DataProviderTooManyArgumentsNoValidationTest.php';
8+
9+
require_once __DIR__ . '/../../bootstrap.php';
10+
(new PHPUnit\TextUI\Application)->run($_SERVER['argv']);
11+
--EXPECTF--
12+
PHPUnit %s by Sebastian Bergmann and contributors.
13+
14+
Runtime: %s
15+
16+
...... 6 / 6 (100%)
17+
18+
Time: %s, Memory: %s
19+
20+
OK (6 tests, 6 assertions)

tests/unit/Metadata/MetadataCollectionTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -589,7 +589,7 @@ private function collectionWithOneOfEach(): MetadataCollection
589589
Metadata::coversFunction(''),
590590
Metadata::coversMethod('', ''),
591591
Metadata::coversNothingOnClass(),
592-
Metadata::dataProvider('', ''),
592+
Metadata::dataProvider('', '', true),
593593
Metadata::dependsOnClass('', false, false),
594594
Metadata::dependsOnMethod('', '', false, false),
595595
Metadata::disableReturnValueGenerationForTestDoubles(),

0 commit comments

Comments
 (0)