Closed (outdated)
Project:
Drupal core
Version:
8.8.x-dev
Component:
simpletest.module
Priority:
Normal
Category:
Feature request
Assigned:
Unassigned
Issue tags:
Reporter:
Created:
25 May 2012 at 23:55 UTC
Updated:
23 Mar 2019 at 11:19 UTC
Jump to comment: Most recent, Most recent file
Comments
Comment #1
sunLet's move this one step further and also adjust the other base assertion functions accordingly.
This means you can pass a message string of
'Yada yada, @first mea culpa @second, tadaa!'to them and will be logged as you'd expect.Comment #3
xjmSee also #1601202: Make simpletest assertions output both the default and custom message texts.
Comment #4
sunFixed the test failures. This pretty much clarifies why #1601202: Make simpletest assertions output both the default and custom message texts won't fly at all.
Comment #5
sunImproved phpDoc and function signature.
Comment #6
sunExample test conversion.
Comment #8
sunRe-rolled against HEAD.
Can we move forward with this patch, or are there any serious objections?
Comment #9
robloachPHPUnit has the same assertSame() and assertNotSame(), so this test makes complete sense. I also like that we're already making use of it in some cases.
Looking at PHPUnit's assertNotSame(), I see why it's not "!==". RTBC.
Comment #10
sunHowever, for full disclosure:
The assertSame() and assertNotSame() methods are not really compatible with PHPUnit's. They happen to share the strict type comparison.
But otherwise, especially the first
$labelargument is custom. The intention is to provide methods that retain the verbose output of"var_export(@first) is identical to var_export(@second)"of assertIdentical(), but additionally allow to prefix that assertion message with a label, so test results can be scanned and debugged more easily.If that is insufficient for certain assertions, then tests are able to use the assert*() methods being improved by this patch - which now allow to customize the assertion message, while keeping the @first/@second/etc placeholders.
In the end, these improvements will allow us to remove almost all calls to t() and format_string() in test assertions.
Comment #11
catchIf we switch to PHPUnit at some point, what will happen to these?
Comment #12
catchComment #13
sunAlright, let's forget about assertSame().
Comment #15
jhodgdonI just marked #1803674: Remove t() from default test assertions in TestBase class as a duplicate of this issue. It was taking a simpler approach...
Comment #16
damien tournoud commentedWhy is this required?
Comment #17
sunAdded docs for the filtering, and fixed test failures.
Comment #17.0
sunUpdated issue summary.
Comment #17.1
sunUpdated issue summary.
Comment #18
sunI've updated the issue summary for the revised/current proposal.
I think this is ready to fly.
Comment #19
jhodgdonI looked at the code here... a couple of comments:
a) Why is this @todo here?
It seems like it would actually be fairly common for one of the tokens to be an ordinary string variable value, so I think you would always need this? Also, some of the previously existing code used var_export on string data, such as this bit from assertNoFieldByXPath:
b) I think it would be useful to document for each $message what placeholders someone can use if they override the default message. For instance on assertTrue/False, document that @value will be replaced by the value in any message, and on assertEqual, @first and @second will be replaced... but maybe that's a separate issue since many of these placeholders already existed.
c) in tearDown()
This message is still being translated. format_plural() does translation.
d) in drupalCreateRole()
Does it still make sense to implode, since var_export() is being used in formatString()? [this applies to other parts of the patch too]
Comment #20
sunMeanwhile, I advanced on this, since I continuously need to re-implement these better assertion messages for individual tests.
Showcasing
assertEqual(), which should actually be part of the proposed newformatString():Comment #21
jwilson3re @20, I might argue that if either the actual OR the expected had a newline, then *both* should be wrapped in PRE, just for consistency of the visual comparison, no?
Comment #22
jhodgdonI am not certain that most/all users of assertEqual put the "actual" value first and the "expected" value second, either? It doesn't seem right to me. assertEqual previously had the semantics of "see if these two values are equal", not "see if this value is what you expect it to be". I don't know that it's always used in the "actual vs. expected" manner. I think it would be better to leave the message as less specific as it used to be.
Comment #23
tstoecklerRe #22: Can you explain in what use-case you would use that function that isn't the actual/expected use-case? Both in my own usage and also when reading other people's code that has been the exclusive one I've seen.
Comment #24
jhodgdonWell, at a minimum I am not sure that "actual" and "expected" are always in the order that is suggested by this modification.
Comment #25
tstoecklerAhh, okay, I can see that. But don't you think that could be a matter of getting used to the new function? IDE people will even get the autocomplete to tell them what to put where. And to me it feels natural to do $actual first and then $expected, because that is the way we do if{}s in core. I.e. we avoid yoda conditions such as
if ('Adam' == $user->name())but instead we doif ($user->name() == 'Adam').Sorry for being a little bit persistent, but I see great value in a function as depicted in #20. I wrote a rather comprehensive suite of tests for Libraries API 2.x which consisted of comparing very long arrays, where the $expected value was (minimally) computed, and even after manually inserting the < pre > tags, as that function also does, it was very hard to remember which browser tab was which, and, by extension whether my test was wrong or whether it was actually showing a legit fail. Another example is you see a failed assertion on "NULL is equal to FALSE." which tells absolutely nothing about what is going on.
Comment #26
jhodgdonHow many core and contrib tests are doing it the other way and would therefore need to be changed?
Comment #27
eric_a commentedNote that PHPUnit signatures have $expected as the first argument.
http://www.phpunit.de/manual/3.8/en/writing-tests-for-phpunit.html#writi...
Comment #28
jhodgdonHuge swaths of the previous patch do not apply any more. And I still think it needs work due to the comments above.
Comment #29
pfrenssenComment #29.0
pfrenssenUpdated issue summary.
Comment #30
mile23I've been writing unit tests for Drupal\simpletest\TestBase. You know, like you do. And t() and other globals make it a pain to test.
I might be hijacking this issue with something a bit out of scope, but regardless of whether its t() or format_string(), this class needs to be unit tested completely, and therefore it can't be a global.
I suggest following the lead of this: https://drupal.org/node/2079611
Basically have a TranslationInterface object as a property, allowing TestBase to have its own
t()method. Then replacet()with$this->t().Doing it correctly allows for dependency injection in unit tests:
The main problem with this approach is that some methods in TestBase are inexplicably static (
insertAssert()mainly), and don't know what$thismeans sot()remains global within them.Static baaaad. :-)
Comment #31
jhodgdon#30 is a completely separate issue. This one is really only about test assertion messages, not about other translations. Please open a new issue. Thanks!
Comment #32
mile23Right. Test assertion messages are currently run through t(). The issue is using str_replace() and format_string() instead, in order to allow predefined placeholders.
I point out that t() and format_string() make it difficult to write unit tests, so please do dependency injection the right way.
Which is not off-topic.
Comment #33
jhodgdonThe title of this issue was misleading, so I have updated it to reflect the current issue summary. I think we have fixed all the test assertions' use of t(), on a separate meta-issue. If there are still t() calls that shouldn't be there, that is a separate issue and should be referencing the other meta-issue.
This issue is specifically about making the base tests assertions have a "base string" argument that would be passed through format_string() so that standard substitutions could be made.
Comment #34
mile23OK. :-)
And here's your other issue.. :-)
#2144669: Improve/Refactor TestBase Through Expanded Unit Testing
Comment #35
olli commentedRe #33: This issue is referenced by #500866: [META] remove t() from assert message.
EDIT: reopened #1803674: Remove t() from default test assertions in TestBase class
Comment #36
sweetchuckI would like to reroll the patch #30, but not exactly clear to me what is the expected result. (Assertion message).
Do we need replace the String::format() with $this->t() ?
And what about the format_plural()?
Comment #37
jhodgdonRE #36, the patch in #30 is out of scope, see #31 ... I really don't know what it was trying to do, but we absolutely do **not** want to be translating test assert messages. We spent a huge amount of effort on several other issues to make sure we were not translating them, and as far as I can tell, the patch in #30 is trying to translate them again. I don't know why.
We probably need to go back to the patch in #17, get it rerolled, and address the comments in #19-#25, rather than starting with the patch in #30.
Comment #38
sunYeah, #30 is strange. All calls to t() have been replaced with String::format() in the meantime.
Just to recap on the original idea here:
Currently, you only have two options:
$this->assertEqual('foo', 'bar');$this->assertEqual('foo', 'bar', 'foo is somewhat equal to bar.');The fundamental idea here is to introduce a third option:
$this->assertEqual('foo', 'bar', '@first is somewhat equal to @second.');The values for
@firstand@secondare the first and second arguments respectively, and automaticallyvar_export()'ed.For assertion methods that only accept a single argument, the placeholder is just simply
@value.This allows you to easily use more meaningful custom assertion messages, while still retaining the output of the expected vs. actual variables in the assertion message (like the default assertion message would do).
Currently, our tests are littered with custom assertion messages that are completely hiding the expected + actual values from the assertion message. Due to that, developers often have to temporarily hack failing tests to remove the custom assertion messages, so as to see what's actually going wrong.
To accomplish this, the patch introduces a
TestBase::formatString()method, which essentiallyString::format(), but additionallyvar_export()s the placeholder values.Comment #39
sweetchuckI think the isset() returns TRUE for empty strings.
The default value of the $message argument in most of the assert*() method is an empty string, which is checked with
if (!isset($message)) {, therefore I changed the check toif (!$message) {.I think it would be better to change the default value of the $message argument to
NULLfrom empty string.Comment #41
sweetchuckComment #42
jhodgdonYeah, a patch that passes the tests! Going forward, please include an interdiff with each new patch, so we can see what changed. For now, we can use the patch in #41 as a baseline.
So, I think this looks pretty good. I have a few suggestions for the documentation:
a) In the formatString() method docs, it refers to format_string() but it actually passes the formatting to String::format().
b) assertTrue() docs:
- Check ==> Checks
c) All assert methods using the new formatString() calls: We need to document what the default message and placeholders are, in the @param $message for each method, so that users of that assertion get this information. And we can probably take out the part that is in all of them about using String::format().
As an example, I would suggest this for assertTrue:
@param $message
(optional) A message to display with the assertion, which defaults to 'Value @value is TRUE.' You can use @value as a placeholder in your message for the $value parameter (it will be passed through var_export() and appropriately formatted). Do not translate messages.
d) I am not sure why some of the messages are using ! and some are using @ for formatting. I think we should pick one and use it consistently.
Hmm... Given the code in the TestBase::formatString() method, which sometimes inserts PRE tags, I think @ is not a good idea. Wouldn't @-formatting take those PRE tags and do check_plain() on them, which would make them not be tags any more? So probably we should be using ! instead of @ in the default messages that are inserting the input variables? Maybe we should test this and see what happens if you do:
assertTrue(array('foo' => 721));
or something like that.
e) Some of the changes in this patch aren't all that great:
We still need to do $this->container->get('url_generator')->generateFromPath() not url() here.
Comment #43
no_angel commented.
Comment #44
jhodgdonWe should still consider doing this, but it would be 8.1 now.
Comment #46
wim leersThis would be so valuable, let's do this!
Comment #47
wim leersComment #54
berdirI believe this is no longer relevant, phpunit assertions displays both the message and the values, maybe some of our legacy/BC assertions could be improved but should mostly be fine.