New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Handling assert() statement failures in PHP 5 and 7. #1897
Comments
I am not sure what you want me to do here, sorry. |
Bit of clarity here - I'm referring mostly to PHP's inbuilt assert() statement http://www.php.net/assert/ , not to the assertions found in unit tests. In the Drupal documentation these assertions are referred to as "runtime assertions" since when they are active they are checked outside the unit tests. They exist as a supplement to unit tests by helping contrib authors call the API correctly when they are writing new code, something unit tests can't do. PHP 7 made significant changes to assert(): https://github.com/tpunt/PHP7-Reference#expectations Most of these are for the better, but they have to be turned on and they present a challenge in writing tests that can pass under PHP 5 or 7 without modification. This issue request centers on creating that compatibility. PHP 5 by default assert() triggers an E_USER_WARNING error whenever its expression evaluates to FALSE. Any unit test needing to test if an assertion fails on cue would need to look for PHPUnit_Framework_Error_Warning under the current system. This works, but it's sloppy. PHP 7 does the same by default, but it also allows assert() failures to be thrown. It does this when the assert.exception flag is set to true, and the throw is \AssertionError. Drupal 8 executes the Handle::register code in the above quoted patch during its PHPUnit bootstrap to get PHP 5 to mimic PHP 7's exception behavior as closely as possible. What I am asking is for PHPUnit to do the same - under PHP 5 register an assert() callback that then throws an \AssertionError, under PHP 7 just set the assert.exception flag to true and let PHP do the throw itself. From there, all unit tests can use either annotation or the setExpectedException function to look for \AssertionError. This handler needs to be attached before the bootstrap file is loaded so existing tests that already register assert callbacks will be able to overwrite this new one without any change to their code, making this a backwards compatible change. |
To be honest, this is an issue with Drupal, not PHPUnit. If Drupal really uses If, on the other hand, Drupal uses |
"If Drupal really uses assert() in it's PHP 5 context in tests, but also Runtime assertions (use of the PHP assert() statement) are a supplement to In Drupal they exist mostly to supplement PHP Type hinting, particularly to As I explained in depth to the Drupal team, Unit tests have the limitation Now while it is true PHP 7 has assertions trigger errors for BC reasons, If, on the other hand, Drupal uses assert() in their normal codebase, then They are used in the Drupal codebase and the unit tests use the PHP 5 On Sun, Feb 7, 2016 at 12:47 AM, phlopsi notifications@github.com wrote:
|
Thanks for clarifying what Drupal uses
Type checkingYou should throw an Also, If you use PHP's integrated parameter type checking for anything, I want to make clear, that nothing of it is checked at compile time, so you could argue that you would have to get rid of every possible type checking and instead use Domain checkingAside from type checking, there is also domain checking. While type checking should always be done where applicable, because it is inexpensive, you may decide to not check the domain of a provided value, that is, validating it's correct state, because this can indeed be expensive to do, e.g. validating a JSON-string. While it seems doable to use The code, where you have to make the decision, is [...]
Situation 1: You do not need to check the domain in your code, because you have to trust your own/project's code. How can you trust that? Write a testsuite for the calling code, so you can be sure, it works as intended. Situation 2: You must check the domain, because you cannot trust external code to work as intended. If you come to a point where you would have to check the domain multiple times and the domain check is expensive, you need a way to cache the validation result in some way. If you work with an array, string or another non-object you should probably introduce a wrapper class.
Since we, from time to time, tend to forget about doing things explicitly (thanks, human brain), having the object represent valid state is preferred, if possible. This also results in less type checking code, inreasing overall readability. Closing wordsI hope my arguments are clear enough to understand and sound reasonable to you, because this is what I've learned from experience over the years about that topic. If you have questions, if you think I have missed something or if you've made different experiences and want to share your opinion and/or knowledge with me, feel free to ask/tell me :) I'll write more comments to get into detail about the other things, because maybe you can already respond to this in the meantime and I don't want to wreck anyone's brain by a gigantic text wall |
In my opinion, return type hinting as of now, is more or less just what it says, a hint. Your tests should already be checking for the correct type to be returned. A On the other hand, if you use |
I'm not inclined to rehash the arguments I had over a six month period in https://www.drupal.org/node/2492225 Some additional points though.
2 Domain checking I respect your opinion greatly, but Test Driven design isn't a universal On Sun, Feb 7, 2016 at 9:32 AM, phlopsi notifications@github.com wrote:
|
I'm definitely not familiar with Drupal, but render array + legacy, that's all I need to know. I've also read your linked post and the need for possible redundant validations, because of the legacy. I can only imagine this nightmare... In short, it was untested code before and when trying to add validation at runtime, it would've killed the performance. In this case, I agree, Back to the original topic: class WarningTest extends \PHPUnit_Framework_TestCase
{
public function testWarning() {
$this->expectException(PHPUnit_Framework_Error_Warning::class);
$this->expectExceptionMessage('I am an assertion!');
assert('false', 'I am an assertion!');
}
} If your requested feature would be implemented this test would fail, in both PHP 5 and 7, even if it ran without any issues before. It might even be the case that the project with this kind of test doesn't support PHP 7 yet, i.e. throwing a pseudo AssertionError might make no sense for them. Blame the legacy ;) But this would only cover the case, in which this is the default. We could also make this configurable via something like if ($emulate_assertion_error) {
if (PHP_MAJOR_VERSION === 5) {
class AssertionError extends Exception
{
public function __construct($message, $code, $previous, $file, $line) { ... }
}
assert_options(ASSERT_WARNING, 0);
assert_options(ASSERT_CALLBACK, function ($file, $line, $assertion, $description) {
throw new AssertionError($description, 0, null, $file, $line);
});
} else {
ini_set('assert.exception', 1);
}
} Something that came to my mind, after I've written all this (damnit) is, does this really benefit other people? If your application, e.g. Drupal, needs to transform the warning to an AssertionError, do you need this functionality in PHPUnit? Your application should already have all this logic, because it uses it, even if only in tests. You might be using more than just PHPUnit or something different entirely. You would still need to implement all this, because one of those test projects does not come with the ability to do this. OT: Can Drupal modules edit the render array in some way, so refactoring woud be a very long process, touching a lot of code? |
I can only repeat what I wrote back in October: I do not understand what you want. Maybe a pull request could help. |
No feedback, closing. |
…d phpunit behavior, possibly sebastianbergmann/phpunit#1897)
The Drupal project has begun using runtime assertions (the PHP assert() statement) to supplement unit tests since they are idea for policing API interactions between disparate units of code. While assert() statements usually don't need unit tests (usually it's redundant, like writing a unit test to test a unit test) occasionally it is necessary. The following patch was applied to Drupal and elements of this will be useful for anyone using PHP Unit alongside assert().
http://cgit.drupalcode.org/drupal/commit/?id=5bb2889
The method in that patch file sets PHP 7's assert.exception flag to 1 so that it will throw exceptions, then adds an assert callback to PHP 5 to do the same. The end result is both versions of PHP throw an \AssertionError when an assert statement is failed, and unit tests can then look for such failures using the existing test annotation or setExpectedException functions. There is the unavoidable slight difference that PHP 5's \AssertionError extends from \Exception, where in PHP 7 it will extend from \Error.
Having this as a default behavior in PHPunit would be highly useful.
The text was updated successfully, but these errors were encountered: