mirror of
https://github.com/pestphp/pest.git
synced 2026-03-06 15:57:21 +01:00
Merge pull request #321 from jordanbrauer/fix-missing-dataset-errors
Add user-friendly exception message for missing test inputs
This commit is contained in:
36
src/Exceptions/DatasetMissing.php
Normal file
36
src/Exceptions/DatasetMissing.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Pest\Exceptions;
|
||||||
|
|
||||||
|
use BadFunctionCallException;
|
||||||
|
use NunoMaduro\Collision\Contracts\RenderlessEditor;
|
||||||
|
use NunoMaduro\Collision\Contracts\RenderlessTrace;
|
||||||
|
use Symfony\Component\Console\Exception\ExceptionInterface;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance of dataset is not present for test that has arguments.
|
||||||
|
*
|
||||||
|
* @internal
|
||||||
|
*/
|
||||||
|
final class DatasetMissing extends BadFunctionCallException implements ExceptionInterface, RenderlessEditor, RenderlessTrace
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create new exception instance.
|
||||||
|
*
|
||||||
|
* @param array<string, string> $args A map of argument names to their typee
|
||||||
|
*/
|
||||||
|
public function __construct(string $file, string $name, array $args)
|
||||||
|
{
|
||||||
|
parent::__construct(sprintf(
|
||||||
|
"A test with the description '%s' has %d argument(s) ([%s]) and no dataset(s) provided in %s",
|
||||||
|
$name,
|
||||||
|
count($args),
|
||||||
|
implode(', ', array_map(static function (string $arg, string $type): string {
|
||||||
|
return sprintf('%s $%s', $type, $arg);
|
||||||
|
}, array_keys($args), $args)),
|
||||||
|
$file,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -228,4 +228,13 @@ final class TestCaseFactory
|
|||||||
|
|
||||||
return $classFQN;
|
return $classFQN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the test case will receive argument input from Pest, or not.
|
||||||
|
*/
|
||||||
|
public function receivesArguments(): bool
|
||||||
|
{
|
||||||
|
return count($this->datasets) > 0
|
||||||
|
|| $this->factoryProxies->count('addDependencies') > 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,11 +5,13 @@ declare(strict_types=1);
|
|||||||
namespace Pest\Repositories;
|
namespace Pest\Repositories;
|
||||||
|
|
||||||
use Closure;
|
use Closure;
|
||||||
|
use Pest\Exceptions\DatasetMissing;
|
||||||
use Pest\Exceptions\ShouldNotHappen;
|
use Pest\Exceptions\ShouldNotHappen;
|
||||||
use Pest\Exceptions\TestAlreadyExist;
|
use Pest\Exceptions\TestAlreadyExist;
|
||||||
use Pest\Exceptions\TestCaseAlreadyInUse;
|
use Pest\Exceptions\TestCaseAlreadyInUse;
|
||||||
use Pest\Exceptions\TestCaseClassOrTraitNotFound;
|
use Pest\Exceptions\TestCaseClassOrTraitNotFound;
|
||||||
use Pest\Factories\TestCaseFactory;
|
use Pest\Factories\TestCaseFactory;
|
||||||
|
use Pest\Support\Reflection;
|
||||||
use Pest\Support\Str;
|
use Pest\Support\Str;
|
||||||
use Pest\TestSuite;
|
use Pest\TestSuite;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
@ -140,6 +142,14 @@ final class TestRepository
|
|||||||
throw new TestAlreadyExist($test->filename, $test->description);
|
throw new TestAlreadyExist($test->filename, $test->description);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$test->receivesArguments()) {
|
||||||
|
$arguments = Reflection::getFunctionArguments($test->test);
|
||||||
|
|
||||||
|
if (count($arguments) > 0) {
|
||||||
|
throw new DatasetMissing($test->filename, $test->description, $arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->state[sprintf('%s%s%s', $test->filename, self::SEPARATOR, $test->description)] = $test;
|
$this->state[sprintf('%s%s%s', $test->filename, self::SEPARATOR, $test->description)] = $test;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,4 +53,20 @@ final class HigherOrderMessageCollection
|
|||||||
$message->call($target);
|
$message->call($target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Count the number of messages with the given name.
|
||||||
|
*
|
||||||
|
* @param string $name A higher order message name (usually a method name)
|
||||||
|
*/
|
||||||
|
public function count(string $name): int
|
||||||
|
{
|
||||||
|
return array_reduce(
|
||||||
|
$this->messages,
|
||||||
|
static function (int $total, HigherOrderMessage $message) use ($name): int {
|
||||||
|
return $total + (int) ($name === $message->name);
|
||||||
|
},
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@ use ReflectionException;
|
|||||||
use ReflectionFunction;
|
use ReflectionFunction;
|
||||||
use ReflectionNamedType;
|
use ReflectionNamedType;
|
||||||
use ReflectionParameter;
|
use ReflectionParameter;
|
||||||
|
use ReflectionUnionType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @internal
|
* @internal
|
||||||
@ -170,4 +171,37 @@ final class Reflection
|
|||||||
|
|
||||||
return $name;
|
return $name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Receive a map of function argument names to their types.
|
||||||
|
*
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
|
public static function getFunctionArguments(Closure $function): array
|
||||||
|
{
|
||||||
|
$parameters = (new ReflectionFunction($function))->getParameters();
|
||||||
|
$arguments = [];
|
||||||
|
|
||||||
|
foreach ($parameters as $parameter) {
|
||||||
|
/** @var ReflectionNamedType|ReflectionUnionType|null $types */
|
||||||
|
$types = ($parameter->hasType()) ? $parameter->getType() : null;
|
||||||
|
|
||||||
|
if (is_null($types)) {
|
||||||
|
$arguments[$parameter->getName()] = 'mixed';
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$arguments[$parameter->getName()] = implode('|', array_map(
|
||||||
|
static function (ReflectionNamedType $type): string {
|
||||||
|
return $type->getName();
|
||||||
|
},
|
||||||
|
($types instanceof ReflectionNamedType)
|
||||||
|
? [$types] // NOTE: normalize as list of to handle unions
|
||||||
|
: $types->getTypes(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $arguments;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -573,6 +573,7 @@
|
|||||||
|
|
||||||
PASS Tests\Unit\TestSuite
|
PASS Tests\Unit\TestSuite
|
||||||
✓ it does not allow to add the same test description twice
|
✓ it does not allow to add the same test description twice
|
||||||
|
✓ it alerts users about tests with arguments but no input
|
||||||
|
|
||||||
PASS Tests\Visual\Help
|
PASS Tests\Visual\Help
|
||||||
✓ visual snapshot of help command output
|
✓ visual snapshot of help command output
|
||||||
@ -600,5 +601,5 @@
|
|||||||
✓ it is a test
|
✓ it is a test
|
||||||
✓ it uses correct parent class
|
✓ it uses correct parent class
|
||||||
|
|
||||||
Tests: 4 incompleted, 9 skipped, 380 passed
|
Tests: 4 incompleted, 9 skipped, 381 passed
|
||||||
|
|
||||||
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Pest\Exceptions\DatasetMissing;
|
||||||
use Pest\Exceptions\TestAlreadyExist;
|
use Pest\Exceptions\TestAlreadyExist;
|
||||||
use Pest\TestSuite;
|
use Pest\TestSuite;
|
||||||
|
|
||||||
@ -7,7 +8,17 @@ it('does not allow to add the same test description twice', function () {
|
|||||||
$testSuite = new TestSuite(getcwd(), 'tests');
|
$testSuite = new TestSuite(getcwd(), 'tests');
|
||||||
$test = function () {};
|
$test = function () {};
|
||||||
$testSuite->tests->set(new \Pest\Factories\TestCaseFactory(__FILE__, 'foo', $test));
|
$testSuite->tests->set(new \Pest\Factories\TestCaseFactory(__FILE__, 'foo', $test));
|
||||||
$this->expectException(TestAlreadyExist::class);
|
|
||||||
$this->expectExceptionMessage(sprintf('A test with the description `%s` already exist in the filename `%s`.', 'foo', __FILE__));
|
|
||||||
$testSuite->tests->set(new \Pest\Factories\TestCaseFactory(__FILE__, 'foo', $test));
|
$testSuite->tests->set(new \Pest\Factories\TestCaseFactory(__FILE__, 'foo', $test));
|
||||||
});
|
})->throws(
|
||||||
|
TestAlreadyExist::class,
|
||||||
|
sprintf('A test with the description `%s` already exist in the filename `%s`.', 'foo', __FILE__),
|
||||||
|
);
|
||||||
|
|
||||||
|
it('alerts users about tests with arguments but no input', function () {
|
||||||
|
$testSuite = new TestSuite(getcwd(), 'tests');
|
||||||
|
$test = function (int $arg) {};
|
||||||
|
$testSuite->tests->set(new \Pest\Factories\TestCaseFactory(__FILE__, 'foo', $test));
|
||||||
|
})->throws(
|
||||||
|
DatasetMissing::class,
|
||||||
|
sprintf("A test with the description '%s' has %d argument(s) ([%s]) and no dataset(s) provided in %s", 'foo', 1, 'int $arg', __FILE__),
|
||||||
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user