-
-
Notifications
You must be signed in to change notification settings - Fork 2.2k
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
backupStaticAttributes
doesn't work properly when class autoloaders are in use (was #1)
#1372
Comments
Consequently, all statics of newly loaded classes were "empty" (as declared by default) before the test was run, and thus, the statics of newly loaded classes should be reset/reverted to their declared default values. Comparing the list of This process needs to skip class properties defined in |
Yes, that would work, if PHPUnit can access the "declared default values". Did you have an implementation in mind for that? (It seems like a rather tricky problem to me, but there's probably something I haven't thought of.) |
Hm… I thought of
Not sure whether that means what it seems to mean; needs manual testing, I guess. |
It works on HHVM, but doesn't work on PHP 5 |
Looks like there's a bug in HHVM 😉 Just tried various options myself, but yes, PHP does not seem to record/track the original default value of static class properties (most likely, all static variables in general). In turn, it looks like there are only two options:
|
This seems too heavyweight to me to be workable. Don't forget that "backupStaticAttributes" is positioned as an alternative to "--process-isolation", which executes each test in its own process, guaranteeing isolation of static variables (but at a huge performance cost, especially on windows).
I don't think this is possible, see my comment re Zend autoloaders on the old issue thread. I can't see a realistic fix for this issue. I think PHPUnit should issue a warning whenever "backupStaticAttributes" is set and it detects that a class was loaded during the course of a test (and that class has static fields):
|
Created sebastianbergmann/phpunit-documentation#226 to at least address this in the manual for the time being. Now, potentially silly consideration. Theoretically…
Obvious constraint: The precondition only applies to unit test suites. It doesn't apply to DI/Web/Acceptance/Selenium/etc test suites. But, could that be a property on |
The manual has been improved for now (not necessarily deployed yet), but regardless of that, this issue kept me thinking recently. As an alternative to my previous comment, please consider this train of thought:
The general idea being:
In theory, the values can be parsed via Tokenizer once per class, retained in a collection of "clean slate" values, and injected into every "backup/restore" operation. That operation is still expensive, but compared to HEAD/master, it's a wash, because HEAD iterates over all declared classes for each test method all over again. Fundamental difference to HEAD/master: All statics that are not part of Would that be a more feasible option? - If time permits, I'll probably try it out in StaticReflection first. |
I am in the process of factoring out the global state backup/restore code into https://github.com/sebastianbergmann/global-state so I'd rather not make changes to the existing implementation inside PHPUnit. |
@sun -- that sounds very complicated and fragile, but I suppose it could work, yes. The saving grace is your point (2): from the docs "Like any other PHP static variable, static properties may only be initialized using a literal or constant; expressions are not allowed". From my experiments, it looks like constant-valued expressions are allowed (e.g. 24 * 60 * 60). @sebastianbergmann - do you have a timescale for the migration? |
I hope to be able to make the changes in |
Initial work on this is here: 59ce590 |
Moved to sebastianbergmann/global-state#4. |
(Moved from issue #1, due to that issue repeatedly being incorrectly auto-closed by GitHub.)
Summary
Example use case
You may dismiss this by saying that the static field didn't exist before the test was run, so "backupStaticAttributes" couldn't catch it, but that does not account for how this feature appears to be intended to operate from the point of view of a PHPUnit user.
If I have a static class which looks like, for example:
... and if I want to modify this static field for the duration of a single test:
Then, as things are documented I should expect the "backupStaticAttributes" feature to cause this field change to be isolated to this single test. This bug means that whichever test first loads the class in question will "win" and its static changes will leak to all other tests in the same run.
Possible fixes and workarounds
It is not clear to me that this is easily fixable, but it is a real bug.
I can't see a "class loaded" event to which PHPUnit could subscribe. Perhaps @eriksencosta 's suggestion of wrapping the autoloader would work. However, any autoloaders added after the wrapping occurred would be missed, which seems quite likely to happen (e.g. Zend controller tests bootstrap Zend in the test "setup" method, at which point a lot of autoloaders are added).
Perhaps the fix should be documentation of the limitations of the "backupStaticAttributes" feature. or perhaps the "backupStaticAttributes" feature needs to be removed as not workable.
Perhaps the
restoreStaticAttributes()
method could check whether any new classes have been autoloaded in the course of the test and, if so, issue a warning that "backupStaticAttributes
" doesn't work in the presence of autoloaders?That wouldn't fix the issue, but at least it would give a warning when it occurred, which might save people lots of time tracking down mysterious bugs that depend on the order in which tests are run.
Isolated test case
See https://github.com/thehereward/phpunit-backup-static-attributes-bug
The text was updated successfully, but these errors were encountered: