clockwork
clockwork copied to clipboard
Incompatibility with PHPUnit 10 (Pest 2) - overriding final method
Attempting to run a test with PHPUnit 10 (or Pest 2) which has the UsesClockwork trait causes the test to fail with the following message:
PHP Fatal error: Cannot override final method PHPUnit\Framework\Assert::assertThat() in /opt/project/vendor/itsgoingd/clockwork/Clockwork/Support/Laravel/Tests/UsesClockwork.php on line 71
According to PHPUnit's changelog:
The public methods of
PHPUnit\Framework\AssertandPHPUnit\Framework\TestCaseare nowfinal
Pest 2 requires PHPUnit 10, so it shares the incompatibility.
+1 here, running into this too. And I love using clockwork with tests!
Edit: Looks like this would need to use the new events system: https://localheinz.com/articles/2023/02/14/extending-phpunit-with-its-new-event-system/#content-events-in-the-new-event-system
Here's an example of a phpunit 10 extension using the new events system (from the article above): https://github.com/localheinz/phpunit-speedtrap/tree/feature/phpunit-10
I got this running again in a very hacky way, without collecting asserts by using the modified UsesClockwork trait below. It at least does collect all of the other information like performance, logging, events, etc. Once you remove the assertThat function that can't be overloaded anymore, there's a few more errors because of PHPUnit\Runner\BaseTestRunner being removed and status methods changing. In the file below I've replaced those with the new classes/functions so at least it runs.
It does seem like with the phpunit 10 update that using events is required here. I really did a very cursory dive into this just now so take all this information with a grain of salt.
<?php
namespace Tests;
use Clockwork\Helpers\Serializer;
use Clockwork\Helpers\StackFilter;
use Clockwork\Helpers\StackTrace;
use PHPUnit\Framework\Constraint\Constraint;
use PHPUnit\Framework\TestStatus\TestStatus;
// Trait to include in PHPUnit tests to collect executed tests
trait UsesClockwork
{
// Clockwork phpunit metadata collected while executing tests
protected static $clockwork = [
'asserts' => [],
];
// Set up Clockwork in this test case, should be called from the PHPUnit setUp method
protected function setUpClockwork()
{
if (! $this->app->make('clockwork.support')->isCollectingTests()) {
return;
}
$this->beforeApplicationDestroyed(function () {
if ($this->app->make('clockwork.support')->isTestFiltered($this->toString())) {
return;
}
$this->app->make('clockwork')
->resolveAsTest(
$this->toString(),
$this->resolveClockworkStatus(),
$this->status()->message(),
$this->resolveClockworkAsserts()
)
->storeRequest();
});
}
// Resolve Clockwork test status
protected function resolveClockworkStatus()
{
$status = $this->status()->asInt();
$statuses = [
TestStatus::unknown()->asInt() => 'unknown',
TestStatus::success()->asInt() => 'success',
TestStatus::skipped()->asInt() => 'skipped',
TestStatus::incomplete()->asInt() => 'incomplete',
TestStatus::failure()->asInt() => 'failure',
TestStatus::error()->asInt() => 'error',
TestStatus::risky()->asInt() => 'risky',
TestStatus::warning()->asInt() => 'warning',
];
return isset($statuses[$status]) ? $statuses[$status] : null;
}
// Resolve executed asserts
protected function resolveClockworkAsserts()
{
$asserts = static::$clockwork['asserts'];
if ($this->status() == TestStatus::failure() && count($asserts)) {
$asserts[count($asserts) - 1]['success'] = false;
}
static::$clockwork['asserts'] = [];
return $asserts;
}
// Overload the main PHPUnit assert method to collect executed asserts
// public static function assertThat($value, Constraint $constraint, string $message = ''): void
// {
// $trace = StackTrace::get(['arguments' => true, 'limit' => 10]);
// $assertFrame = $trace->filter(function ($frame) {
// return strpos($frame->function, 'assert') === 0;
// })->last();
// $trace = $trace->skip(StackFilter::make()->isNotVendor(['itsgoingd', 'phpunit']))->limit(3);
// static::$clockwork['asserts'][] = [
// 'name' => $assertFrame->function,
// 'arguments' => $assertFrame->args,
// 'trace' => (new Serializer)->trace($trace),
// 'passed' => true,
// ];
// parent::assertThat($value, $constraint, $message);
// }
}
Hey, I wrote some very suspect php code and implemented a new Clockwork extension for PHPUnit 10. I would appreciate if you could try it out, since I don't have any good PHPUnit 10 test-suite for testing.
To try the new extension, update Clockwork to dev-master, remove the UsesClockwork trait and add following to your phpunit.xml:
<extensions>
<bootstrap class="Clockwork\Support\Laravel\Tests\ClockworkExtension" />
</extensions>
Just tried it out and ran some tests and it is working great so far! This is awesome!!! Thank you so much, I love this project.
If I run into anything I'll let you know.
I tested it as well. Works great! Thanks for making this work.
Hi. Do i have to configure something else after enabling 'collect' => env('CLOCKWORK_TESTS_COLLECT', true), and using the extension?
I'm runing php artisan test and php artisan test --filter="SomeControllerTest" and its not working.
Thanks ;)
@walterdis Yeah, adding the PHPUnit extension and setting CLOCKWORK_TESTS_COLLECT=true should work. If it doesn't work, can you try updating to the latest master?
@walterdis Yeah, adding the PHPUnit extension and setting
CLOCKWORK_TESTS_COLLECT=trueshould work. If it doesn't work, can you try updating to the latest master?
Worked!! Thanks a lot ;)
Now available in Clockwork 5.2.