mirror of
https://github.com/pestphp/pest.git
synced 2026-07-24 18:40:03 +02:00
fix: report dataset provider errors as failing tests (#1749)
Errors raised while resolving a test's dataset (missing named dataset, throwing dataset closure) previously crashed the whole run. Now the data provider catches them, wraps them in a DatasetProviderError, and the test method rethrows the original throwable so the affected test fails cleanly with a non-zero exit code while other tests keep running.
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Pest\Exceptions;
|
||||||
|
|
||||||
|
use RuntimeException;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class DatasetProviderError extends RuntimeException
|
||||||
|
{
|
||||||
|
public function __construct(Throwable $previous)
|
||||||
|
{
|
||||||
|
parent::__construct($previous->getMessage(), (int) $previous->getCode(), $previous);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -161,6 +161,7 @@ final class TestCaseFactory
|
|||||||
$classCode = <<<PHP
|
$classCode = <<<PHP
|
||||||
namespace $namespace;
|
namespace $namespace;
|
||||||
|
|
||||||
|
use Pest\Exceptions\DatasetProviderError as __PestDatasetProviderError;
|
||||||
use Pest\Repositories\DatasetsRepository as __PestDatasets;
|
use Pest\Repositories\DatasetsRepository as __PestDatasets;
|
||||||
use Pest\TestSuite as __PestTestSuite;
|
use Pest\TestSuite as __PestTestSuite;
|
||||||
|
|
||||||
|
|||||||
@@ -235,6 +235,10 @@ final class TestCaseMethodFactory
|
|||||||
$attributesCode
|
$attributesCode
|
||||||
public function $methodName(...\$arguments)
|
public function $methodName(...\$arguments)
|
||||||
{
|
{
|
||||||
|
if (count(\$arguments) === 1 && \$arguments[0] instanceof __PestDatasetProviderError) {
|
||||||
|
throw \$arguments[0]->getPrevious() ?? \$arguments[0];
|
||||||
|
}
|
||||||
|
|
||||||
return \$this->__runTest(
|
return \$this->__runTest(
|
||||||
\$this->__test,
|
\$this->__test,
|
||||||
...\$arguments,
|
...\$arguments,
|
||||||
@@ -261,7 +265,11 @@ final class TestCaseMethodFactory
|
|||||||
|
|
||||||
public static function $dataProviderName()
|
public static function $dataProviderName()
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
return __PestDatasets::get(self::\$__filename, "$methodName");
|
return __PestDatasets::get(self::\$__filename, "$methodName");
|
||||||
|
} catch (\Throwable \$throwable) {
|
||||||
|
return [[new __PestDatasetProviderError(\$throwable)]];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EOF;
|
EOF;
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
dataset('throws', function () {
|
||||||
|
throw new RuntimeException('boom from dataset');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('x', function ($a) {
|
||||||
|
expect($a)->toBeTrue();
|
||||||
|
})->with('throws');
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
it('references a missing dataset', function ($value) {
|
||||||
|
expect($value)->toBeTruthy();
|
||||||
|
})->with('missing');
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
it('passes normally', function () {
|
||||||
|
expect(true)->toBeTrue();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('references a missing dataset', function ($value) {
|
||||||
|
expect($value)->toBeTruthy();
|
||||||
|
})->with('missing');
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Symfony\Component\Process\Process;
|
||||||
|
|
||||||
|
$run = function (string $target): array {
|
||||||
|
$process = new Process(
|
||||||
|
['php', 'bin/pest', $target],
|
||||||
|
dirname(__DIR__, 2),
|
||||||
|
['COLLISION_PRINTER' => 'DefaultPrinter', 'COLLISION_IGNORE_DURATION' => 'true'],
|
||||||
|
);
|
||||||
|
|
||||||
|
$process->run();
|
||||||
|
|
||||||
|
return [
|
||||||
|
'output' => removeAnsiEscapeSequences($process->getOutput().$process->getErrorOutput()),
|
||||||
|
'code' => $process->getExitCode(),
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
test('reports missing datasets as errors for a single file run', function () use ($run) {
|
||||||
|
$result = $run('tests/.tests/IssueOnly.php');
|
||||||
|
|
||||||
|
expect($result['output'])
|
||||||
|
->toContain("A dataset with the name `missing` does not exist. You can create it using `dataset('missing', ['a', 'b']);`.")
|
||||||
|
->toContain('FAILED')
|
||||||
|
->toContain('Tests: 1 failed');
|
||||||
|
|
||||||
|
expect($result['code'])->not->toBe(0);
|
||||||
|
})->skipOnWindows();
|
||||||
|
|
||||||
|
test('reports missing datasets as errors alongside passing tests', function () use ($run) {
|
||||||
|
$result = $run('tests/.tests/IssueWithPassing.php');
|
||||||
|
|
||||||
|
expect($result['output'])
|
||||||
|
->toContain("A dataset with the name `missing` does not exist. You can create it using `dataset('missing', ['a', 'b']);`.")
|
||||||
|
->toContain('1 passed')
|
||||||
|
->toContain('1 failed');
|
||||||
|
|
||||||
|
expect($result['code'])->not->toBe(0);
|
||||||
|
})->skipOnWindows();
|
||||||
|
|
||||||
|
test('reports dataset closure exceptions as errors', function () use ($run) {
|
||||||
|
$result = $run('tests/.tests/DatasetClosureThrows.php');
|
||||||
|
|
||||||
|
expect($result['output'])
|
||||||
|
->toContain('boom from dataset');
|
||||||
|
|
||||||
|
expect($result['code'])->not->toBe(0);
|
||||||
|
})->skipOnWindows();
|
||||||
Reference in New Issue
Block a user