diff --git a/src/Functions.php b/src/Functions.php index 2ae4274e..fca2b5a4 100644 --- a/src/Functions.php +++ b/src/Functions.php @@ -235,8 +235,10 @@ if (! function_exists('covers')) { /** @var \Pest\Mutate\Repositories\ConfigurationRepository $configurationRepository */ $configurationRepository = Container::getInstance()->get(ConfigurationRepository::class); $everything = $configurationRepository->cliConfiguration->toArray()['everything'] ?? false; + $classes = $configurationRepository->cliConfiguration->toArray()['classes'] ?? false; + $paths = $configurationRepository->cliConfiguration->toArray()['paths'] ?? false; - if ($runner->isEnabled() && ! $everything) { + if ($runner->isEnabled() && ! $everything && ! is_array($classes) && ! is_array($paths)) { $beforeEachCall->only('__pest_mutate_only'); } } diff --git a/src/PendingCalls/TestCall.php b/src/PendingCalls/TestCall.php index f675dddd..1b02ed1e 100644 --- a/src/PendingCalls/TestCall.php +++ b/src/PendingCalls/TestCall.php @@ -9,7 +9,6 @@ use Pest\Exceptions\InvalidArgumentException; use Pest\Exceptions\TestDescriptionMissing; use Pest\Factories\Attribute; use Pest\Factories\TestCaseMethodFactory; -use Pest\Mutate\Contracts\Configuration; use Pest\Mutate\Repositories\ConfigurationRepository; use Pest\PendingCalls\Concerns\Describable; use Pest\Plugins\Only; @@ -554,9 +553,13 @@ final class TestCall ); } - /** @var Configuration $configuration */ - $configuration = Container::getInstance()->get(ConfigurationRepository::class)->globalConfiguration('default'); // @phpstan-ignore-line - $configuration->class(...$classes); // @phpstan-ignore-line + /** @var ConfigurationRepository $configurationRepository */ + $configurationRepository = Container::getInstance()->get(ConfigurationRepository::class); + $paths = $configurationRepository->cliConfiguration->toArray()['paths'] ?? false; + + if (! is_array($paths)) { + $configurationRepository->globalConfiguration('default')->class(...$classes); // @phpstan-ignore-line + } return $this; } @@ -573,9 +576,13 @@ final class TestCall ); } - /** @var Configuration $configuration */ - $configuration = Container::getInstance()->get(ConfigurationRepository::class)->globalConfiguration('default'); // @phpstan-ignore-line - $configuration->class(...$traits); // @phpstan-ignore-line + /** @var ConfigurationRepository $configurationRepository */ + $configurationRepository = Container::getInstance()->get(ConfigurationRepository::class); + $paths = $configurationRepository->cliConfiguration->toArray()['paths'] ?? false; + + if (! is_array($paths)) { + $configurationRepository->globalConfiguration('default')->class(...$traits); // @phpstan-ignore-line + } return $this; } @@ -657,6 +664,7 @@ final class TestCall if ($this->description !== null) { $this->description .= ' → '; } + $this->description .= $arguments === null ? $name : sprintf('%s %s', $name, $exporter->shortenedRecursiveExport($arguments)); diff --git a/src/Plugins/Help.php b/src/Plugins/Help.php index 9bb1f51d..0d5ed55a 100644 --- a/src/Plugins/Help.php +++ b/src/Plugins/Help.php @@ -158,6 +158,62 @@ final readonly class Help implements HandlesArguments 'desc' => 'Set the minimum required coverage percentage, and fail if not met', ], ...$content['Code Coverage']]; + $content['Mutation Testing'] = [[ + 'arg' => '--mutate ', + 'desc' => 'Runs mutation testing, to understand the quality of your tests', + ], [ + 'arg' => '--mutate --parallel', + 'desc' => 'Runs mutation testing in parallel', + ], [ + 'arg' => '--mutate --min', + 'desc' => 'Set the minimum required mutation score, and fail if not met', + ], [ + 'arg' => '--mutate --id', + 'desc' => 'Run only the mutation with the given ID. But E.g. --id=ecb35ab30ffd3491. Note, you need to provide the same options as the original run', + ], [ + 'arg' => '--mutate --covered-only', + 'desc' => 'Only generate mutations for classes that are covered by tests', + ], [ + 'arg' => '--mutate --bail', + 'desc' => 'Stop mutation testing execution upon first untested or uncovered mutation', + ], [ + 'arg' => '--mutate --class', + 'desc' => 'Generate mutations for the given class(es). E.g. --class=App\\\\Models', + ], [ + 'arg' => '--mutate --ignore', + 'desc' => 'Ignore the given class(es) when generating mutations. E.g. --ignore=App\\\\Http\\\\Requests', + ], [ + 'arg' => '--mutate --clear-cache', + 'desc' => 'Clear the mutation cache', + ], [ + 'arg' => '--mutate --no-cache', + 'desc' => 'Clear the mutation cache', + ], [ + 'arg' => '--mutate --ignore-min-score-on-zero-mutations', + 'desc' => 'Ignore the minimum score requirement when there are no mutations', + ], [ + 'arg' => '--mutate --ignore-min-score-on-zero-mutations', + 'desc' => 'Ignore the minimum score requirement when there are no mutations', + ], [ + 'arg' => '--mutate --covered-only', + 'desc' => 'Only generate mutations for classes that are covered by tests', + ], [ + 'arg' => '--mutate --everything', + 'desc' => 'Generate mutations for all classes, even if they are not covered by tests', + ], [ + 'arg' => '--mutate --profile', + 'desc' => 'Output to standard output the top ten slowest mutations', + ], [ + 'arg' => '--mutate --retry', + 'desc' => 'Run untested or uncovered mutations first and stop execution upon first error or failure', + ], [ + 'arg' => '--mutate --stop-on-uncovered', + 'desc' => 'Stop mutation testing execution upon first untested mutation', + ], [ + 'arg' => '--mutate --stop-on-untested', + 'desc' => 'Stop mutation testing execution upon first untested mutation', + ]]; + $content['Profiling'] = [ [ 'arg' => '--profile ', diff --git a/tests/.pest/snapshots/Visual/Help/visual_snapshot_of_help_command_output.snap b/tests/.pest/snapshots/Visual/Help/visual_snapshot_of_help_command_output.snap index d26aa818..7539a107 100644 --- a/tests/.pest/snapshots/Visual/Help/visual_snapshot_of_help_command_output.snap +++ b/tests/.pest/snapshots/Visual/Help/visual_snapshot_of_help_command_output.snap @@ -124,6 +124,26 @@ --disable-coverage-ignore ...... Disable metadata for ignoring code coverage --no-coverage Ignore code coverage reporting configured in the XML configuration file + MUTATION TESTING OPTIONS: + --mutate .... Runs mutation testing, to understand the quality of your tests + --mutate --parallel ...................... Runs mutation testing in parallel + --mutate --min Set the minimum required mutation score, and fail if not met + --mutate --id Run only the mutation with the given ID. But E.g. --id=ecb35ab30ffd3491. Note, you need to provide the same options as the original run + --mutate --covered-only Only generate mutations for classes that are covered by tests + --mutate --bail Stop mutation testing execution upon first untested or uncovered mutation + --mutate --class Generate mutations for the given class(es). E.g. --class=App\\Models + --mutate --ignore Ignore the given class(es) when generating mutations. E.g. --ignore=App\\Http\\Requests + --mutate --clear-cache ............................ Clear the mutation cache + --mutate --no-cache ............................... Clear the mutation cache + --mutate --ignore-min-score-on-zero-mutations Ignore the minimum score requirement when there are no mutations + --mutate --ignore-min-score-on-zero-mutations Ignore the minimum score requirement when there are no mutations + --mutate --covered-only Only generate mutations for classes that are covered by tests + --mutate --everything Generate mutations for all classes, even if they are not covered by tests + --mutate --profile . Output to standard output the top ten slowest mutations + --mutate --retry Run untested or uncovered mutations first and stop execution upon first error or failure + --mutate --stop-on-uncovered Stop mutation testing execution upon first untested mutation + --mutate --stop-on-untested Stop mutation testing execution upon first untested mutation + PROFILING OPTIONS: --profile .............. Output to standard output the top ten slowest tests diff --git a/tests/Features/Covers.php b/tests/Features/Covers.php index c7721aa8..386d523f 100644 --- a/tests/Features/Covers.php +++ b/tests/Features/Covers.php @@ -48,12 +48,12 @@ it('uses the correct PHPUnit attribute for trait', function () { it('uses the correct PHPUnit attribute for covers nothing', function () { $attributes = (new ReflectionMethod($this, $this->name()))->getAttributes(); - expect($attributes[2]->getName())->toBe('PHPUnit\Framework\Attributes\CoversNothing'); - expect($attributes[2]->getArguments())->toHaveCount(0); + expect($attributes[3]->getName())->toBe('PHPUnit\Framework\Attributes\CoversNothing'); + expect($attributes[3]->getArguments())->toHaveCount(0); })->coversNothing(); it('throws exception if no class nor method has been found', function () { $testCall = new TestCall(TestSuite::getInstance(), 'filename', 'description', fn () => 'closure'); $testCall->covers('fakeName'); -})->throws(InvalidArgumentException::class, 'No class or method named "fakeName" has been found.'); +})->throws(InvalidArgumentException::class, 'No class, trait or method named "fakeName" has been found.');