Closed PrinsFrank closed 3 years ago
Hi, the behavior you are describing is expected: you are testing that header()
raises an Exception, but this only happens when headers are already sent, which they never are with our NullPrinter.
See:
$ php -r 'header("foo");'
$ php -r 'echo PHP_EOL;header("foo");'
PHP Warning: Cannot modify header information - headers already sent by (output started at Command line code:1) in Command line code on line 1
In other words, your test suite is coupled with the testing tool, which should never be.
I am not actually asserting that the exception is thrown. The header call and the exception catching is actually done in code in laravel controllers and middleware in our codebase, but I moved it to the test function to make a minimal reproduction. the assertTrue on true is only to demonstrate a passing test and not make phpunit mark this as risky.
Besides the fact that we shouldn't set headers in controllers this way in laravel - which I have since removed - there does seem to be an issue in the combination of paratest and phpunit;
An even more minimalistic repo (which is marked as risky becaus no assertions are done):
<?php
class FooTest extends PHPUnit\Framework\TestCase
{
public function testFoo(): void
{
try {
header('oeaoeabxuroae');
exit;
} catch (\Exception $e) {
}
}
}
I suspect there is an issue in phpunit itself that expects the presenter to write something to the output buffer, which fails when the presenter doesn't write anything but code hit in a test does. I've looked at all 'ob_*' functions in both phpunit and paratest but can't really find any obvious buffering bugs.
The workaround of actually printing/echoing in the NullPhpunitPrinter could be a quick fix so tests that hit code modifying the output buffer can run using paratest. Fixing the original issue in phpunit (And maybe getting the NullPhpunitPrinter into phpunit itself) needs some help of the devs of phpunit.
I've personally spent quite some time on figuring out why my testcase wouldn't run in paratest but succesfully runs in phpunit, so even when this issue is closed without changes it might help the next person.
Let me clarify what's happening in more details in both cases:
vendor/bin/phpunit FooTest.php
startsPHPUnit 9.5.8 by Sebastian Bergmann and contributors.
is printed to STDOUT
: from this point no header()
can be calledheader()
call raises a PHP Warning: Cannot modify header information - headers already sent
Exceptiontry {} catch {}
block skips the exit
call and proceeds the test as normalWith the NullPrinter, even in PHPUnit (so no ParaTest involved):
vendor/bin/phpunit --printer 'ParaTest\Runners\PHPUnit\Worker\NullPhpunitPrinter' FooTest.php
startsSTDOUT
never receive any stream dataheader()
call does its job without errorsexit;
call exits the entire computer processAs you can see, the test suite is coupled with the testing tool, which should never be.
header()
is a bad beast.
Summary
Paratest fails on a single test, that is able to run fine in phpunit itself. There is a dataprovider with 1200 routes in there, that are visited by the controllertest, so the test runs for a while.
Current behavior
Paratest fails on a single test.
How to reproduce: command, code and error stack trace
When testing a bunch of controllers, some legacy controllers that set a header and then exit fail in paratest where they pass in phpunit itself. A simplified testcase:
Test runs fine in phpunit:
But fails in paratest:
Expected behavior
The test should pass as it does so in phpunit itself.
Debugging result
Disabling the printer line in
ParaTest\Runners\PHPUnit\ExecutableTest
makes paratest pass:Diving into ParaTest\Runners\PHPUnit\Worker\NullPhpunitPrinter I can see only one difference with the other result printers, and that is related to output buffering. Changing this single line makes paratest pass as well: