Problem/Motivation
The assertTrue() and assertFalse() methods have a very specific meaning in PHPUnit: a strong === comparison with TRUE/FALSE. Because of historic reasons we didn't have assertEmpty() and assertNotEmpty() methods on old Simpletest kernel tests, that's why we abused assertTrue/False() for the purposes of checking if something is empty.
This is problematic because developers knowing PHPUnit expect assertFalse() to do a strong comparison, which is important in security related tests for example.
Proposed resolution
Now that we have converted our kernel tests to PHPUnit we should deprecate the assertTrue/False() compatibility override from AssertLegacyTrait.php and fix invocations that rely on the old meaning to assertEmpty() or assertNotEmpty() or assertNull().
Remaining tasks
Fix test fails
User interface changes
none.
API changes
The meaning of assertTrue/False() changes on PHPUnit tests.
Data model changes
none.
| Comment | File | Size | Author |
|---|---|---|---|
| #143 | 2742585-hotfix-143.patch | 516 bytes | tim.plunkett |
| #137 | 2742585-9.0.x-137.patch | 440.99 KB | alexpott |
| #137 | 2742585-8.9.x-137.patch | 441.05 KB | alexpott |
| #135 | 2742585-9.0.x-135.patch | 440.99 KB | alexpott |
| #133 | 2742585-2-133.patch | 441.05 KB | alexpott |
Comments
Comment #2
klausiHere is a start. Let's hope we can convert everything in this issue and the patch does not grow too much. Otherwise we need to open child issues.
Comment #4
klausiSome more fixes, but not done yet so this will fail.
This also revealed the first real test code bug in FieldImportCreateTest where we have wrong assertTrue() calls with 3 parameters.
Comment #6
klausiAnd another round of fixes, still not done.
Comment #9
sutharsan commentedFixing failing tests may be daunting, but for someone with sufficient PHP experience it may be a good task.
Comment #10
jeroenbegyn commentedMe & @tomasnagy are working on this at DrupalCon Dublin 2016.
Comment #11
tomasnagy commentedWent over all the failing test together with @jeroenbegyn. All tests should validate correctly.
Comment #12
tomasnagy commentedComment #14
tomasnagy commentedComment #15
tomasnagy commentedFixed almost all failed tests introduced with 8.3.x. FrontPageTest keeps failing for now, can't figure out why it doesn't know about assertNotEmpty (PHPUnit specific functions?). Any assistance would be appreciated.
Comment #16
tomasnagy commentedComment #18
dawehnerConceptually I totally support this change. One question though we could ask: How do announce that change, so contrib/custom code can adapt without breaking immediately.
Comment #19
kristiaanvandeneyndeFor now, how do I do a hard assertFalse()? Calling parent::assertFalse() seems to put me inside of the trait.
Comment #20
klausiFor now you need to do
to get a type safe assertion for FALSE.
Comment #21
kristiaanvandeneyndeCool, thanks klausi!
Comment #23
zeip commentedRe-rolled the patch against 8.4.x. Let's see how this goes.
Comment #25
zeip commentedMade the change in a few other places also, and removed the change from core/modules/views/src/Tests/AssertViewsCacheTagsTrait.php to fix the problem with FrontPageTest.
Comment #27
kristiaanvandeneyndeGreat progress, thanks ZeiP
Comment #28
zeip commentedSome more tests changed to use more correct assert methods.
Comment #30
zeip commentedThis should fix almost all of the remaining problems; only one test I was unable to run locally. Let's see how it goes.
Comment #32
zeip commentedOkay, so apparently we have two tests of which one works at a time: "Drupal\node\Tests\Views\FrontPageTest", which can't find assertNotEmpty(), and "Drupal\Tests\views\Kernel\RenderCacheIntegrationTest", which requires the change. Both of these use core/modules/views/src/Tests/AssertViewsCacheTagsTrait.php.
Back to square one with an updated patch for 8.4.x. I'll try to continue with this in the coming days. Assigning to myself.
Comment #33
zeip commentedThe status shouldn't have been changed.
Comment #35
zeip commentedComment #36
jofitzPatch in #30 no longer applies so re-rolled.
Comment #38
zeip commentedI have to say, I'm somewhat surprised. There's way more failures than I'd have thought.
I'll try to fix some of them tonight, let's see where that gets us.
Comment #39
zeip commentedThis should be much closer. I'm changing the version back to 8.4.x, because I think that making this kind of test cleanup might be a good fit for the RC stage.
Comment #41
zeip commentedThis should be pretty close.
Comment #43
zeip commentedRetry with correct patch.
Comment #45
zeip commentedPerhaps this time it's the actually correct patch. Sorry for the spam.
Comment #46
dawehnerNo worries about the spamming. @ZeiP I'm curious, how do you produce the patch?
Comment #47
zeip commentedI'm making the changes by hand using the testing results, then adding the changed lines with git add -p (trying to catch any possible mistakes I might've made) and then output git diff --cached to the patch file. On the last round I forgot the git add before outputting the ”new” patch... :)
Comment #48
dawehner@ZeiP I'm wondering whether you could use a regex with sed or so to produce the same patch. I don't say that's better or worse, I'm just wondering :)
Comment #49
zeip commentedProbably not. I'm only replacing assertFalse/assertTrue calls that fail, since many of them probably are indeed comparing booleans and should be left alone. And the assertFalse calls are replaced with either assertEmpty or assertNull depending on the value etc. Also I think I should have most of them down again now, probably the same problematic ones to be solved after that – and that probably requires something completely else :)
Thanks for the suggestion, though! The previous issue I was meddling with was a more straightforward one and that one I managed to mostly do with just sed and related tools.
Comment #51
zeip commentedFew more. Perhaps now we have all?
Comment #53
zeip commentedSuspected as much. One more to go. I'm a bit surprised that the constant errors we got a few months ago seem to be gone – perhaps this one'll be a green one?
Comment #54
zeip commentedThere were a few calls to assertTrue that didn't look quite right. This patch tries to fix those to work correctly.
Comment #56
zeip commentedAnother try at fixing the assertTrue in CommentAdminViewUpdateTest.
Comment #57
zeip commentedOk, this seems good to go. Attached is a interdiff for the last change that was more than just a simple assert function change. Please review.
Comment #58
kristiaanvandeneyndeAwesome work ZeiP!
Not to be a pain, but there are also instances of
$this->assertSame(FALSE, ...and$this->assertSame(TRUE, ...that could perhaps be changed while you're at it.Comment #59
fellaroon commentedConceptually I totally support this change. One question though we could ask: How do announce that change, so contrib/custom code can adapt without breaking immediately.
Comment #60
zeip commented@kristiaanvandeneynde, I could change those, but the patch is quite large as is, so I think we should make that a separate issue. I'm not even sure about the few changes in #56, since even those were perhaps slightly out of scope. Keeping the issue straightforward it's probably easier to get in.
Announcing the change is a good question. This also relates to the version question: Should we only do this in 8.5.x?
Comment #61
kristiaanvandeneynde8.5.x only with a change record should make sure people have time to update their code? If people were doing a hard check for TRUE or FALSE, they would use assertSame() already. If people were getting (un)lucky because they meant to use assertFalse and an empty value actually passed their test, their tests will now rightfully fail.
Or am I missing something?
Comment #62
zeip commentedMostly yes. The main point is that apparently assertFalse/assertTrue was in some point actually meant to check for any kind of empty – FALSE, NULL, etc. So I'm not sure if all the tests would then rightfully fail, but they obviously would be now incorrect and therefore should be fixed. Basically IMO announcing this with the brand-new 8.5.x seems fair enough.
Changing the version back to 8.5.x. Attached is a patch for 8.5.x, seems that the previous patch didn't apply cleanly.
Comment #64
zeip commentedApparently there had been added one instance to 8.5.x :) This should be ready for review.
Comment #65
dawehnerWe certainly need to write a change notice so contrib module authors can fix their tests ... I think its okay to
I think this should actually be
$this->assertSame(0, $count)don't you think so?Given what preg_match returns I guess assertSame(1) would be better
I'm curious whether we can be more specific?
... note: I've not reviewed the entire patch but it feels like using assertEmpty everywhere doesn't really improve the situation.
Comment #66
kristiaanvandeneyndeI don't think so. The code used to check for a "true-ish" count; i.e.: the query having results.
Comment #67
zeip commentedThere are indeed quite a bit of instances that should perhaps actually do something else than use any of the assert{False,True,Empty,NotEmpty,Null}. It would be possible to fix these while we're at it, but I'm wondering if that goes a bit too much outside the issue scope and makes reviewing the patch needlessly difficult?
The current patch tries to stay faithful to the original meaning of the assert call fixing just the True/False-assertion, except for two instances which IMO were clearly buggy. I somewhat disagree with @dawehner in that the patch greatly improves the situation for most of the calls, since now we're actually separating booleans from other values. It's only this limited number of calls that are basically in the same situation as before.
If we want to do all of the fixes in this issue, I'm quite fine with it, but IMO it seems like we should maybe create a follow-up task for that and keep this one as simple as possible (which unfortunately still is far from simple).
Comment #68
kristiaanvandeneyndeI tend to agree with Daniel here. If we are going to fix it, might as well fix it now. Otherwise, how will we find those that need fixing in a follow-up?
Comment #69
zeip commentedWell, you are correct in that it would require going through the same changes again.
Attached is a patch trying to find the correct assert functions for each replacement. Optimally this should come out as green, but there's probably something I misinterpreted, so let's see.
Comment #71
zeip commentedFew typos fixed.
Comment #73
zeip commentedA few missing castings and for some reason some previously unproblematic assertTrue/False calls fixed.
Comment #75
zeip commentedOne more error.
Comment #77
zeip commentedSome more fixes for the new changes.
Comment #78
zeip commentedEverything seems to be in order. Attached is an interdiff for the changes, please review.
Comment #80
zeip commentedDamn, named the interdiff incorrectly. Resetting to Needs review.
Comment #81
kristiaanvandeneyndeInterdiff generally looks great!
Just a minor nitpick:
Typecasting return values with (bool) and then asserting their boolean value is essentially the same as checking for empty or not empty. Because 'foobar' would still pass the above test. So those typecasts need to go :/
I'm sorry if this discourages you, because I really feel your pain writing long patches like this, but... Needs work :(
Comment #85
alexpottWow this is a tonne of work - thanks @ZeiP. So the good news is we do have a way of progressing here. We can deprecate instead of remove. I've re-rolled the patch and added deprecations instead of removing the old methods. I guess this will fail with new deprecations as we'll have added new usages. Also we need a change record to inform people about the change.
No interdiff because the conflicts on reroll were many!
Comment #86
alexpottUpdated issue summary. Also this is not just about Kernel tests - it's about Functional and FunctionalJavascript tests too because they use the same trait via
\Drupal\FunctionalTests\AssertLegacyTraitComment #88
alexpottHere's some of the tests fixed and also one bug in \Drupal\user\Plugin\views\access\Role::access() which is quite important because this is an access function which is not adhering to the interface. Should probably split that out into a separate issue but it kinda proves the point about the danger of these overrides.
Comment #90
alexpottComment #91
mondrakeI think this needs a CR, and maybe bumped to critical since this is a deprceation that will impact contrib a lot?
Comment #92
alexpott@mondrake yep,,, thanks for tagging...
This is bug fix. NULL is not a valid value according to config schema. One interesting thing to work out is how come the schema is not being applied. Needs its own issue I think. This issue should be test changes only.
This is bugfix too and needs an issue. This one feels quite serious as it's used in access and it's not returning the correct type. Bring on return typing!
Hence the
https://www.drupal.org/node/TODOComment #93
krzysztof domański1. Needs title update. The title applies only to kernel tests.
2. Should we also improve tests on the simpletest module? The
preg_match()function returns 1, 0 or FALSE.Comment #94
mondrakeper #91 and #92
Comment #95
alexpottComment #96
krzysztof domańskiComment #97
krzysztof domańskiI added change record: Removed assertTrue/False() compatibility overrides in PHPUnit Kernel, Functional and FunctionalJavascript tests.
Comment #98
mondrakeAFAICT it's not 'Remove' but 'Deprecate' - valid for both the issue title and the CR
Comment #99
alexpottUpdated the issue title and will rework the change record.
Comment #100
alexpottI've fleshed out the change record and improved a couple of the new assertions - this task is pretty much unending because there many many places where we could use better assertions so I think we should just get the patch to pass and do our best to use the best semantic assertion in place but not quibble if the new assertion works. Whatever we replace it with it is likely to be more specific that the current duck-typing of HEAD.
@Krzysztof Domański what do you mean by #93.2 - the reason that can't use assertRegExp() is that we need $matches.
Comment #101
alexpottOpened #3082287: \Drupal\user\Plugin\views\access\Role::access() does not conform to the base class documentation and #3082289: \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem::onDependencyRemoval() sets auto_create to an invalid data type to fix the bugs so this issue is only scoped to test changes.
Comment #102
mondrakeHow about deprecating also the
assertEqualsoverride in KernelTestBase and BrowserTestBase, so forcing tests to call explicitlycastSafeStringswhen needed? (I know, not here)Comment #103
alexpottre #102 - sure but yeah not here and the gain with it is less obvious.One thing that is interesting is instead of the assertEquals hack we could register a Comparator to compare MarkupInterface objects... but as before definitely not as part of this issue :)
Comment #104
mondrakeFiled #3082340: Replace assertEquals() overrides in PHPUnit Kernel, Functional and FunctionalJavascript tests with a MarkupInterfaceComparator implementation
Comment #105
krzysztof domańskiI created follow-up: #3082415: Replace assert(Not)Same/Identical() on booleans with assert(Not)True/False() in PHPUnit tests.
Comment #106
krzysztof domańskiI've created a issue to track this and similar problems #3082735: [META] Identify dangerous and problematic cases of casting data types.
Comment #107
alexpottRerolled now the two bug fixes are in. So we should be good to go here unless some new assertTrue/False have been added.
Comment #108
alexpottDoing
on a branch with this patch committed shows that all the changes in the this patch are to code contain in a
/tests/directory apart from the two test traits above. Those traits are test code. They probably should be moved to a better place but that is out-of-scope for this issue.Comment #109
mondrakeNits:
This is removed and not replaced
Same
Looks like the return value of isDefaultRevision is mixed and not bool as documented. Follow up to correct the docs? Then assertNotFalse instead?
assertSame?
assertSame?
Swap the arguments
assertSame?
Swap arguments
Comment #110
alexpott@mondrake re #109
1. This is a duplicate assertion. This assertion is made twice in the test. We just remove one.
2. This is a duplicate assertion. This assertion is made twice in the test. We just remove one.
3. Nah - the cast is unnecessary here. Nice catch. ::isDefaultRevision() can only ever return a Boolean.
4, 5, 6, 7, 8: I don't think matter at all. The assertEquals on a text string is fine. It's not as if
->assertEquals('test', TRUE)results in a pass. And the argument order is wrong all over the place - we shouldn't fix that here we'd be here forever and not get the main win of this issue which is consistency with PHPUnit and catching the occasional bug when we've made assumptions about return types.Comment #111
mondrake#110.3 - One more left.
This can also be fixed on commit. Follow-up #3082415: Replace assert(Not)Same/Identical() on booleans with assert(Not)True/False() in PHPUnit tests filed. RTBC when it turns back green.
Review of #3082340: Replace assertEquals() overrides in PHPUnit Kernel, Functional and FunctionalJavascript tests with a MarkupInterfaceComparator implementation would be appreciated ;)
Comment #112
mondrakeMmm, maybe #111 cannot be fixed on commit after all, it's code change.
Comment #113
mondrakeFixing #111. Minor change aligned with the rest of #110.3, I think I can RTBC.
Comment #114
catchNeeds a re-roll. This is always going to be a problem with a 400+kb patch but also can't really think of a good way to split it up.
Comment #115
mondrakeReroll.
Comment #116
mondrakeComment #118
mondrakeRemoved usage that was added since #113.
Comment #119
mondrake... and the patch ...
Comment #120
mondrakeComment #121
alexpottAs this is a large and disruptive change no point committing it right before the alpha - hopefully this will get it right after.
Comment #123
mondrakeneeds reroll
Comment #124
mondrakeReroll. The @trigger_error for assertTrue and assertFalse in AssertLegacyTrait needed to be moved to the compatibility layer.
Comment #125
mondrakeComment #126
mondrakeComment #127
mondrakeComment #128
mondrakeComment #129
mondrakeComment #130
sokru commentedReroll to 8.9, note that
core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.phpwas already fixed in #2233595Comment #131
sokru commentedComment #133
alexpottFixing the last fail...
Comment #134
mondrakeComment #135
alexpottWe now need 8.x.x and 9.0.x versions because of a change in core/modules/system/tests/src/Functional/Theme/TwigTransTest.php due to Twig 2.
The patch in #133 is for 8.x.x and the patch attached here is for 9.0.x.
Comment #136
catchNeeds one more re-roll. I'm hoping to commit this today just before the beta is tagged:
Comment #137
alexpottRerolled.
Comment #138
catchAdding commit credit..
Comment #140
catchCommitted/pushed to 9.0.x, 8.9.x and 8.8.x, thanks!
Comment #143
tim.plunkettHotfix for the fail caused by this not retesting after #3082655: Specify the $defaultTheme property in all functional tests landed.
Comment #145
xjmComment #146
xjmI committed the hotfix after @lauriii confirmed that it fixes the test. Didn't wait for QA because HEAD is broken and we need to have QA be green to do the beta release. So DrupalCI will give us our green test run in this case.
Thanks @tim.plunkett for the hotfix!
Comment #147
xjm