Re: [Python-Dev] r87389 - in python/branches/py3k: Doc/library/unittest.rst Lib/unittest/case.py Misc/NEWS
On Sat, 18 Dec 2010 21:00:04 +0100 (CET)
ezio.melotti
Author: ezio.melotti Date: Sat Dec 18 21:00:04 2010 New Revision: 87389
Log: #10573: use actual/expected consistently in unittest methods.
IMHO, this should be reverted. The API currently doesn't treat these arguments differently, so they should really be labeled "first" and "second". Otherwise, the user will wrongly assume that the signature is asymmetric and that they should be careful about which order they pass the arguments in. Regards Antoine.
On 12/18/2010 3:48 PM, Antoine Pitrou wrote:
On Sat, 18 Dec 2010 21:00:04 +0100 (CET) ezio.melotti
wrote: Author: ezio.melotti Date: Sat Dec 18 21:00:04 2010 New Revision: 87389
Log: #10573: use actual/expected consistently in unittest methods.
Change was requested by M. Foord and R. Hettinger (and G.Brandl for b2).
IMHO, this should be reverted. The API currently doesn't treat these arguments differently, so they should really be labeled "first" and "second". Otherwise, the user will wrongly assume that the signature is asymmetric and that they should be careful about which order they pass the arguments in.
The error report on assert failure *is* often asymmetrical ;=). From Michael's post: "This is particularly relevant for the methods that produce 'diffed' output on failure - as the order determines whether mismatched items are missing from the expected or additional to the expected." This change struck me as a nice bit of polishing. -- Terry Jan Reedy
I may be unique, but I fear there is no great answer. On the one hand
I almost always code it as e.g. assertEqual(actual, expected), which
matches my preference for e.g. "if x == 5:" rather than "if 5 == x:".
On the other hand in those assert* functions that show a nice diff of
two lists, when reading such a diff my expectation is that "old, new"
corresponds to "expected, actual". Which then freaks me out until I
realize that I coded it as "actual, expected"... And yet "expected,
actual" still looks weird to me. :-(
On Sat, Dec 18, 2010 at 2:46 PM, Terry Reedy
On 12/18/2010 3:48 PM, Antoine Pitrou wrote:
On Sat, 18 Dec 2010 21:00:04 +0100 (CET) ezio.melotti
wrote: Author: ezio.melotti Date: Sat Dec 18 21:00:04 2010 New Revision: 87389
Log: #10573: use actual/expected consistently in unittest methods.
Change was requested by M. Foord and R. Hettinger (and G.Brandl for b2).
IMHO, this should be reverted. The API currently doesn't treat these arguments differently, so they should really be labeled "first" and "second". Otherwise, the user will wrongly assume that the signature is asymmetric and that they should be careful about which order they pass the arguments in.
The error report on assert failure *is* often asymmetrical ;=). From Michael's post: "This is particularly relevant for the methods that produce 'diffed' output on failure - as the order determines whether mismatched items are missing from the expected or additional to the expected."
This change struck me as a nice bit of polishing.
-- Terry Jan Reedy
_______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/guido%40python.org
-- --Guido van Rossum (python.org/~guido)
On Sat, 18 Dec 2010 20:23:49 -0800, Guido van Rossum
I may be unique, but I fear there is no great answer. On the one hand I almost always code it as e.g. assertEqual(actual, expected), which matches my preference for e.g. "if x =3D=3D 5:" rather than "if 5 =3D=3D x:= ". On the other hand in those assert* functions that show a nice diff of two lists, when reading such a diff my expectation is that "old, new" corresponds to "expected, actual". Which then freaks me out until I realize that I coded it as "actual, expected"... And yet "expected, actual" still looks weird to me. :-(
You aren't unique, I feel the same way. But it seems to me that the most important thing is to be consistent, so that I don't freak out for long. -- R. David Murray www.bitdance.com
On Sat, 18 Dec 2010 20:23:49 -0800
Guido van Rossum
I may be unique, but I fear there is no great answer. On the one hand I almost always code it as e.g. assertEqual(actual, expected), which matches my preference for e.g. "if x == 5:" rather than "if 5 == x:". On the other hand in those assert* functions that show a nice diff of two lists, when reading such a diff my expectation is that "old, new" corresponds to "expected, actual". Which then freaks me out until I realize that I coded it as "actual, expected"... And yet "expected, actual" still looks weird to me. :-(
This could be nicely resolved by renaming the arguments "a" and "b", and having the diff display "a, b". It's quite natural (both the diff ordering and the arguments ordering), and they are consistent with each other. Regards Antoine.
On Sun, Dec 19, 2010 at 5:13 AM, Antoine Pitrou
On Sat, 18 Dec 2010 20:23:49 -0800 Guido van Rossum
wrote: I may be unique, but I fear there is no great answer. On the one hand I almost always code it as e.g. assertEqual(actual, expected), which matches my preference for e.g. "if x == 5:" rather than "if 5 == x:". On the other hand in those assert* functions that show a nice diff of two lists, when reading such a diff my expectation is that "old, new" corresponds to "expected, actual". Which then freaks me out until I realize that I coded it as "actual, expected"... And yet "expected, actual" still looks weird to me. :-(
This could be nicely resolved by renaming the arguments "a" and "b", and having the diff display "a, b". It's quite natural (both the diff ordering and the arguments ordering), and they are consistent with each other.
So 'a' stands for 'after' and 'b' for 'before', right? :-) -- --Guido van Rossum (python.org/~guido)
Le dimanche 19 décembre 2010 à 10:41 -0800, Guido van Rossum a écrit :
On Sun, Dec 19, 2010 at 5:13 AM, Antoine Pitrou
wrote: On Sat, 18 Dec 2010 20:23:49 -0800 Guido van Rossum
wrote: I may be unique, but I fear there is no great answer. On the one hand I almost always code it as e.g. assertEqual(actual, expected), which matches my preference for e.g. "if x == 5:" rather than "if 5 == x:". On the other hand in those assert* functions that show a nice diff of two lists, when reading such a diff my expectation is that "old, new" corresponds to "expected, actual". Which then freaks me out until I realize that I coded it as "actual, expected"... And yet "expected, actual" still looks weird to me. :-(
This could be nicely resolved by renaming the arguments "a" and "b", and having the diff display "a, b". It's quite natural (both the diff ordering and the arguments ordering), and they are consistent with each other.
So 'a' stands for 'after' and 'b' for 'before', right? :-)
Ouch. I guess I don't natively think in English.
On Dec 19, 2010, at 10:41 AM, Guido van Rossum wrote:
On Sun, Dec 19, 2010 at 5:13 AM, Antoine Pitrou
wrote: On Sat, 18 Dec 2010 20:23:49 -0800 Guido van Rossum
wrote: I may be unique, but I fear there is no great answer. On the one hand I almost always code it as e.g. assertEqual(actual, expected), which matches my preference for e.g. "if x == 5:" rather than "if 5 == x:". On the other hand in those assert* functions that show a nice diff of two lists, when reading such a diff my expectation is that "old, new" corresponds to "expected, actual". Which then freaks me out until I realize that I coded it as "actual, expected"... And yet "expected, actual" still looks weird to me. :-(
This could be nicely resolved by renaming the arguments "a" and "b", and having the diff display "a, b". It's quite natural (both the diff ordering and the arguments ordering), and they are consistent with each other.
So 'a' stands for 'after' and 'b' for 'before', right? :-)
If you go down the a / b path instead of actual/expected, the diffs are straight-forward but some of the other output styles needed to be changed also (replace the messages for "unexpected" and "missing" elements to "things in a but not in b" and "things in b but not in a". Raymond
On 12/19/2010 1:41 PM, Guido van Rossum wrote:
On Sun, Dec 19, 2010 at 5:13 AM, Antoine Pitrou
wrote:
This could be nicely resolved by renaming the arguments "a" and "b", and having the diff display "a, b". It's quite natural (both the diff ordering and the arguments ordering), and they are consistent with each other.
So 'a' stands for 'after' and 'b' for 'before', right? :-)
difflib uses 'a' and 'b' for before and after (orig,new in svn terms, with edits/diffs from a to b) respectively. Not really great. The docs then have to explain what 'a' and 'b' are and the implications for interpreting the output. -- Terry Jan Reedy
On Sun, 19 Dec 2010 18:54:55 -0500
Terry Reedy
On 12/19/2010 1:41 PM, Guido van Rossum wrote:
On Sun, Dec 19, 2010 at 5:13 AM, Antoine Pitrou
wrote: This could be nicely resolved by renaming the arguments "a" and "b", and having the diff display "a, b". It's quite natural (both the diff ordering and the arguments ordering), and they are consistent with each other.
So 'a' stands for 'after' and 'b' for 'before', right? :-)
difflib uses 'a' and 'b' for before and after (orig,new in svn terms, with edits/diffs from a to b) respectively. Not really great.
For a non-native English speaker, 'a' and 'b' don't evoke 'after' and 'before' but simply the first two letters of the latin alphabet, and their ordering is therefore obvious with respect to function arguments. By the way, hg uses a/b as well, and so does git apparently, so Python's difflib is not exotic in that regard: $ hg diff diff -r 56867877575b README --- a/README Fri Dec 17 21:43:27 2010 +0100 +++ b/README Mon Dec 20 01:42:57 2010 +0100 @@ -1,3 +1,4 @@ +some change This is Python version 3.2 beta 1 ================================= Regards Antoine.
Antoine Pitrou wrote:
For a non-native English speaker, 'a' and 'b' don't evoke 'after' and 'before' but simply the first two letters of the latin alphabet, and their ordering is therefore obvious with respect to function arguments.
It's not just non-native English speakers either. I too think of a, b as being first, second rather than after, before. -- Steven
On 19/12/2010 19:55, Raymond Hettinger wrote:
On Dec 19, 2010, at 10:41 AM, Guido van Rossum wrote:
On Sat, 18 Dec 2010 20:23:49 -0800 Guido van Rossum
wrote: I may be unique, but I fear there is no great answer. On the one hand I almost always code it as e.g. assertEqual(actual, expected), which matches my preference for e.g. "if x == 5:" rather than "if 5 == x:". On the other hand in those assert* functions that show a nice diff of two lists, when reading such a diff my expectation is that "old, new" corresponds to "expected, actual". Which then freaks me out until I realize that I coded it as "actual, expected"... And yet "expected, actual" still looks weird to me. :-( This could be nicely resolved by renaming the arguments "a" and "b", and having the diff display "a, b". It's quite natural (both the diff ordering and the arguments ordering), and they are consistent with each other. So 'a' stands for 'after' and 'b' for 'before', right? :-) If you go down the a / b path instead of actual/expected,
On Sun, Dec 19, 2010 at 5:13 AM, Antoine Pitrou
wrote: the diffs are straight-forward but some of the other output styles needed to be changed also (replace the messages for "unexpected" and "missing" elements to "things in a but not in b" and "things in b but not in a".
Ah man, we've *nearly* finished bikeshedding about the names of unittest assert methods so its time to move onto the names and order of the arguments. Really? I wouldn't use a/b but first/second [1] as they have a more obvious meaning. However, I'm reluctant to move away from the actual/expected terminology. It's standard terminology for testing (used by all the other unit testing frameworks I looked at phpunit, JUnit and NUnit), but more importantly it is a useful way to think about testing - and one used by most devs I've worked with. You fetch an 'actual' result by calling your code and compare it against a pre-computed 'expected' result. Hopefully the two are the same. Talking about your actual value and your expected value is a natural way to talk in testing, so it's a useful concept. Once you use the 'actual' and 'expected' terminology you have a natural order for displaying failure message results: if an element is present in your actual but not in your expected then it is extra. If an element is in your expected but not in your actual then it is missing. Straightforward. (Of course it maybe that your actual is correct and it is your expected result needs correcting, that doesn't affect how failure messages should be presented though.) The only thing left to decide is then the order - (actual, expected) or (expected, actual). Raymond, myself, Guido and Ezio have all expressed a preference for (actual, expected). I like this comment from Raymond on the relevant issue [2]: I also tend to use actual/expected and find the reverse to be a form Yoda-speak, "assert 5 == x", "perhaps misread the prophecy was", etc. As the current ordering used within unittest is (actual, expected), to reverse it would be dumb (why should everyone using the current ordering reformat all their tests for the new order?). So, -1 on dropping actual and expected. They're standard and useful terminology / concepts for testing. If we do move to a more "agnostic" wording in the failure messages (whilst keeping actual / expected as argument names and in the documentation perhaps?) then I prefer first / second to a / b. All the best, Michael Foord [1] Interestingly unittest did use (first, second) for assert argument names back in 2.1 when it was added: http://svn.python.org/view/python/branches/release21-maint/Lib/unittest.py?revision=24536&view=markup [2] http://bugs.python.org/issue10573
Raymond
_______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe:http://mail.python.org/mailman/options/python-dev/fuzzyman%40voidspace.org.u...
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessinghttp://www.sqlite.org/different.html
Le lundi 20 décembre 2010 à 13:00 +0000, Michael Foord a écrit :
Ah man, we've *nearly* finished bikeshedding about the names of unittest assert methods so its time to move onto the names and order of the arguments. Really?
Apparently someone decided this bikeshedding was important enough to make an SVN commit out of it. If you think it isn't worth discussing then perhaps it wasn't worth changing in the first place :)
The only thing left to decide is then the order - (actual, expected) or (expected, actual). Raymond, myself, Guido and Ezio have all expressed a preference for (actual, expected). I like this comment from Raymond on the relevant issue [2]:
I also tend to use actual/expected and find the reverse to be a form Yoda-speak, "assert 5 == x", "perhaps misread the prophecy was", etc.
Isn't it some kind of ethnocentric comment? "Natural" order is not the same in all languages, and I don't see why "actual" should come before "expected". And the problem here is that (actual, expected) is in reverse order of the diff displayed on error. Now if you look at various TypeErrors raised in Python, the error message is most often "expected <some type>, got <other type>". So there expected always comes before actual, and apparently it was natural to the authors of that code. Perhaps they are all Yoda-speakers. And moreover, some methods such as assertRaises() already have their args in the (expected, actual) order.
If we do move to a more "agnostic" wording in the failure messages (whilst keeping actual / expected as argument names and in the documentation perhaps?) then I prefer first / second to a / b.
Well, no. The whole point of displaying results as a/b (or first/second) is that they map intuitively to the first and second args. If, however, you name those args "actual" and "expected", it becomes confusing. Also, it wouldn't fix that your diff would still be in a weird order compared to the method args (unless you think it's fine to display an "expected - actual" diff). Regards Antoine.
On 20/12/2010 13:47, Antoine Pitrou wrote:
Ah man, we've *nearly* finished bikeshedding about the names of unittest assert methods so its time to move onto the names and order of the arguments. Really? Apparently someone decided this bikeshedding was important enough to make an SVN commit out of it. If you think it isn't worth discussing
Le lundi 20 décembre 2010 à 13:00 +0000, Michael Foord a écrit : then perhaps it wasn't worth changing in the first place :)
The only change was to use them consistently and the only code change was to re-order the arguments in a method that is new and not in any previous version of Python. You're arguing for a much bigger change.
The only thing left to decide is then the order - (actual, expected) or (expected, actual). Raymond, myself, Guido and Ezio have all expressed a preference for (actual, expected). I like this comment from Raymond on the relevant issue [2]:
I also tend to use actual/expected and find the reverse to be a form Yoda-speak, "assert 5 == x", "perhaps misread the prophecy was", etc. Isn't it some kind of ethnocentric comment? "Natural" order is not the same in all languages, and I don't see why "actual" should come before "expected".
Agreement that actual, expected was preferred came from an American, a Dutchman, an Englishman and an Italian. :-) As it is what unittest currently uses anyway you'll need more than "I don't see why" to reverse it.
And the problem here is that (actual, expected) is in reverse order of the diff displayed on error.
Diffing is completely an implementation detail of how the failure messages are generated. The important thing is that failure messages make sense with respect to actual result and expected result.
Now if you look at various TypeErrors raised in Python, the error message is most often "expected<some type>, got<other type>". So there expected always comes before actual, and apparently it was natural to the authors of that code. Perhaps they are all Yoda-speakers.
And moreover, some methods such as assertRaises() already have their args in the (expected, actual) order.
If we do move to a more "agnostic" wording in the failure messages (whilst keeping actual / expected as argument names and in the documentation perhaps?) then I prefer first / second to a / b. Well, no. The whole point of displaying results as a/b (or first/second) is that they map intuitively to the first and second args. If, however, you name those args "actual" and "expected", it becomes confusing.
Also, it wouldn't fix that your diff would still be in a weird order compared to the method args (unless you think it's fine to display an "expected - actual" diff).
I told you how I think the diff should be generated and gave the reasons for it. If we use first / second in failure messages (but not necessarily in documentation or argument names - your objections notwithstanding) then the messages for the container asserts don't need to imply a direction (as Raymond suggested, wording like "in first but not in second" and "in second but not in first"). The string diffs are more problematic as they actually use difflib to generate the failure output. I'm also against dropping the use of actual / expected concepts and terminology within unittest as I think they are useful. We don't necessarily need to use them in the failure outputs but it seems like you want them to be dropped altogether. All the best, Michael
Regards
Antoine.
_______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/fuzzyman%40voidspace.org.u...
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
Le lundi 20 décembre 2010 à 14:03 +0000, Michael Foord a écrit :
On 20/12/2010 13:47, Antoine Pitrou wrote:
Ah man, we've *nearly* finished bikeshedding about the names of unittest assert methods so its time to move onto the names and order of the arguments. Really? Apparently someone decided this bikeshedding was important enough to make an SVN commit out of it. If you think it isn't worth discussing
Le lundi 20 décembre 2010 à 13:00 +0000, Michael Foord a écrit : then perhaps it wasn't worth changing in the first place :)
The only change was to use them consistently and the only code change was to re-order the arguments in a method that is new and not in any previous version of Python. You're arguing for a much bigger change.
No, I'm first of all arguing for a "first/second" or "a/b" argument naming. Which was exactly the case before the change that triggered this thread.
The only thing left to decide is then the order - (actual, expected) or (expected, actual). Raymond, myself, Guido and Ezio have all expressed a preference for (actual, expected). I like this comment from Raymond on the relevant issue [2]:
I also tend to use actual/expected and find the reverse to be a form Yoda-speak, "assert 5 == x", "perhaps misread the prophecy was", etc. Isn't it some kind of ethnocentric comment? "Natural" order is not the same in all languages, and I don't see why "actual" should come before "expected".
Agreement that actual, expected was preferred came from an American, a Dutchman, an Englishman and an Italian. :-)
I'm not sure what that's supposed to prove, unless you have problems with the idea that what is natural for a couple of people isn't natural for everyone. You also apparently missed that part:
Now if you look at various TypeErrors raised in Python, the error message is most often "expected<some type>, got<other type>". So there expected always comes before actual, and apparently it was natural to the authors of that code. Perhaps they are all Yoda-speakers.
As it is what unittest currently uses anyway you'll need more than "I don't see why" to reverse it.
unittest doesn't "use it anyway", since it used first/second before that change. Actually, as I pointed out, (expected, actual) is used in assertRaises in friends.
And the problem here is that (actual, expected) is in reverse order of the diff displayed on error.
Diffing is completely an implementation detail of how the failure messages are generated. The important thing is that failure messages make sense with respect to actual result and expected result.
Which, again, they don't. Let's see: self.assertEqual(actual, expected) AssertionError: 'a\nb\nc\ne\n' != 'a\nb\nc\nd\n' a b c - e + d The diff shows "expected - actual", but it would be logical (in your own logic) to display "actual - expected". The whole issue disappears if you drop this idea of naming the arguments "actual" and "expected".
I'm also against dropping the use of actual / expected concepts and terminology within unittest as I think they are useful. We don't necessarily need to use them in the failure outputs but it seems like you want them to be dropped altogether.
I'm saying that they cause confusion wrt. to the actual error display (as Guido also admitted). Feel free to come up with a solution that doesn't get rid of actual/expected, if that's what you want ;) Regards Antoine.
On 12/18/2010 04:46 PM, Terry Reedy wrote:
On 12/18/2010 3:48 PM, Antoine Pitrou wrote:
On Sat, 18 Dec 2010 21:00:04 +0100 (CET) ezio.melotti
wrote: Author: ezio.melotti Date: Sat Dec 18 21:00:04 2010 New Revision: 87389
Log: #10573: use actual/expected consistently in unittest methods.
Change was requested by M. Foord and R. Hettinger (and G.Brandl for b2).
IMHO, this should be reverted. The API currently doesn't treat these arguments differently, so they should really be labeled "first" and "second". Otherwise, the user will wrongly assume that the signature is asymmetric and that they should be careful about which order they pass the arguments in.
I've always presumed it would make a difference in error displayed anyway.
The error report on assert failure *is* often asymmetrical ;=). From Michael's post: "This is particularly relevant for the methods that produce 'diffed' output on failure - as the order determines whether mismatched items are missing from the expected or additional to the expected."
This change struck me as a nice bit of polishing.
I like ("actual", "expected") in the asserts. It matches my expected ordering of "input"/"output" and how I use comparisons in 'if' statements. I feel it is more important that the diffs are consistent with other diffs in python. So (for me), changing the asymmetrical output to be symmetrical would be in the category of foolish consistency because changing that, introduces other inconsistencies I'd rather not have. It doesn't bother me that the functions arguments aren't the same order of the diffs as long as the labels and wording are obvious enough in the messages. So maybe the diff output can be improved a bit instead of changing the terms and ordering. Ron
On 12/20/2010 6:31 AM, Antoine Pitrou wrote:
Diffing is completely an implementation detail of how the failure messages are generated. The important thing is that failure messages make sense with respect to actual result and expected result. Which, again, they don't. Let's see:
self.assertEqual(actual, expected) AssertionError: 'a\nb\nc\ne\n' != 'a\nb\nc\nd\n' a b c - e + d
The diff shows "expected - actual", but it would be logical (in your own logic) to display "actual - expected". The whole issue disappears if you drop this idea of naming the arguments "actual" and "expected".
I'm not a unittest user, although I probably will become one, in time, when I learn enough to contribute to Python, instead of just find bugs in it from use. I don't much care what the parameters names are, although the terms actual and expected seem good for testing scenarios if properly used, but the above does not match what I would expect the behavior to be from a testing scenario: run the test, and tell me what changed from the expected results. If the expected result (not parameter) is d and the actual result (not parameter) is e, the diff should show a b c - d + e Thinking-that-sometimes-a-novice's-expectations-are-relevant-to-such-discussions'ly yours, Glenn
On Mon, Dec 20, 2010 at 1:55 AM, Steven D'Aprano
Antoine Pitrou wrote:
For a non-native English speaker, 'a' and 'b' don't evoke 'after' and 'before' but simply the first two letters of the latin alphabet, and their ordering is therefore obvious with respect to function arguments.
It's not just non-native English speakers either. I too think of a, b as being first, second rather than after, before.
I was mostly being facetious (my main point being there's no perfect solution here), though I *have* seen serious code using the b=before/a=after convention. -- --Guido van Rossum (python.org/~guido)
On Tue, Dec 21, 2010 at 1:31 AM, Antoine Pitrouwrote: >> Diffing is completely an implementation detail of how the failure >> messages are generated. The important thing is that failure messages >> make sense with respect to actual result and expected result. > > Which, again, they don't. Let's see: > > self.assertEqual(actual, expected) > AssertionError: 'a\nb\nc\ne\n' != 'a\nb\nc\nd\n' > a > b > c > - e > + d > > The diff shows "expected - actual", but it would be logical (in your own > logic) to display "actual - expected". The whole issue disappears if you > drop this idea of naming the arguments "actual" and "expected". To make this a bit clearer... >>> class Ex(ut.TestCase): ... def demo(self): ... self.assertEqual("actual", "expected") ... >>> Ex("demo").demo() Traceback (most recent call last): AssertionError: 'actual' != 'expected' - actual + expected For the actual/expected terminology the diff is the wrong way around (as of 3.2b1, anyway). My own +1 goes to keeping the actual/expected terminology (and ordering) and adjusting the diffs accordingly (with a header noting that the diff is old=expected, new=actual). assertRaises() *is* an exception to the general actual/expected pattern, but that asymmetry is forced by the ability to pass arbitrary positional arguments to the function being tested (which later proved useful for the context manager form as well). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On 21/12/2010 01:57, Nick Coghlan wrote: > On Tue, Dec 21, 2010 at 1:31 AM, Antoine Pitrouwrote: >>> Diffing is completely an implementation detail of how the failure >>> messages are generated. The important thing is that failure messages >>> make sense with respect to actual result and expected result. >> Which, again, they don't. Let's see: >> >> self.assertEqual(actual, expected) >> AssertionError: 'a\nb\nc\ne\n' != 'a\nb\nc\nd\n' >> a >> b >> c >> - e >> + d >> >> The diff shows "expected - actual", but it would be logical (in your own >> logic) to display "actual - expected". The whole issue disappears if you >> drop this idea of naming the arguments "actual" and "expected". > To make this a bit clearer... > >>>> class Ex(ut.TestCase): > ... def demo(self): > ... self.assertEqual("actual", "expected") > ... >>>> Ex("demo").demo() > Traceback (most recent call last): > > AssertionError: 'actual' != 'expected' > - actual > + expected > > For the actual/expected terminology the diff is the wrong way around > (as of 3.2b1, anyway). > The recent commit that sparked the controversy was supposed to ensure that all the asserts were documented consistently *and* worked as per the documentation. The error above is from assertMultiLineEqual. assertListEqual has the same issue: >>> t.assertListEqual([1], [2]) Traceback (most recent call last): ... AssertionError: Lists differ: [1] != [2] First differing element 0: 1 2 - [1] + [2] Interestingly assertSetEqual already uses the first/second symmetric wording: >>> t.assertSetEqual({1}, {2}) ... AssertionError: Items in the first set but not the second: 1 Items in the second set but not the first: 2 > My own +1 goes to keeping the actual/expected terminology (and > ordering) and adjusting the diffs accordingly (with a header noting > that the diff is old=expected, new=actual). > Well we don't have consensus. Whatever we do we need to be consistent, and in the absence of an agreement about a change we should at least make all the behaviour and documentation consistent. From this discussion and the discussion on the issue tracker: Myself, Nick Coghlan and Ezio Melotti prefer (actual, expected) Raymond like (actual, expected) but would be happy with symmetrical diffs Guido prefers the (actual, expected) ordering but prefers diffs to show the other way round R David Murray agreed with Guido Terry Reedy liked the change Glenn Linderman wants (actual, expected) and diffing to follow that Ron Adam ditto Symmetrical diffs (element in first not in second, element in second not in first) solves the problem without imposing an order on the arguments. Actually unittest *has* used (first, second) to refer to the arguments to asserts pretty much since its inception. Losing the (actual, expected) terminology is a cost of this but unittest hasn't emphasised this terminology in the past (as I thought it had). This won't work for diffing strings (assertMultiLineEqual) which use difflib and needs a direction for the diff. As above it is currently the wrong way round for (actual, expected). The other alternative is to make them consistent and follow Nick's suggestion adding the header note to the diffs that old=expected, new=actual. > assertRaises() *is* an exception to the general actual/expected > pattern, but that asymmetry is forced by the ability to pass arbitrary > positional arguments to the function being tested (which later proved > useful for the context manager form as well). The (actual, expected) pattern matches the way almost everyone I've ever seen write if statements and asserts: if x == 5: rather than if 5 == x: assert x == 5 rather than assert 5 == x It also matches functions like isinstance and issubclass. On the other hand it doesn't match the way we report TypeErrors where we report "expected , got ". All the best, Michael Foord > Cheers, > Nick. > -- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
On 12/21/2010 4:17 AM, Michael Foord wrote:
Glenn Linderman wants (actual, expected) and diffing to follow that
If you say that is what I said, fine. I might not have understood the example well enough to say the right thing. I liked Nick's explanation, using the actual and expected words in his example, but -expected +actual -old +new is what I would expect in the diff. I didn't say anything about the parameters, I don't care, except that the documentation leads me to use it correctly, so that I get the above diff results. Sadly, if the diff results are not as above, I would probably misuse the parameters to achieve it, unless doing something for which the standard is to do it "backwards".
On Tue, Dec 21, 2010 at 4:17 AM, Michael Foord
On 21/12/2010 01:57, Nick Coghlan wrote:
On Tue, Dec 21, 2010 at 1:31 AM, Antoine Pitrou
wrote: Diffing is completely an implementation detail of how the failure messages are generated. The important thing is that failure messages make sense with respect to actual result and expected result.
Which, again, they don't. Let's see:
self.assertEqual(actual, expected) AssertionError: 'a\nb\nc\ne\n' != 'a\nb\nc\nd\n' a b c - e + d
The diff shows "expected - actual", but it would be logical (in your own logic) to display "actual - expected". The whole issue disappears if you drop this idea of naming the arguments "actual" and "expected".
To make this a bit clearer...
class Ex(ut.TestCase):
... def demo(self): ... self.assertEqual("actual", "expected") ...
Ex("demo").demo()
Traceback (most recent call last): <snip TB details> AssertionError: 'actual' != 'expected' - actual + expected
For the actual/expected terminology the diff is the wrong way around (as of 3.2b1, anyway).
The recent commit that sparked the controversy was supposed to ensure that all the asserts were documented consistently *and* worked as per the documentation. The error above is from assertMultiLineEqual.
assertListEqual has the same issue:
t.assertListEqual([1], [2]) Traceback (most recent call last): ... AssertionError: Lists differ: [1] != [2]
First differing element 0: 1 2
- [1] + [2]
Interestingly assertSetEqual already uses the first/second symmetric wording:
t.assertSetEqual({1}, {2}) ... AssertionError: Items in the first set but not the second: 1 Items in the second set but not the first: 2
My own +1 goes to keeping the actual/expected terminology (and ordering) and adjusting the diffs accordingly (with a header noting that the diff is old=expected, new=actual).
Well we don't have consensus. Whatever we do we need to be consistent, and in the absence of an agreement about a change we should at least make all the behaviour and documentation consistent.
From this discussion and the discussion on the issue tracker:
Myself, Nick Coghlan and Ezio Melotti prefer (actual, expected) Raymond like (actual, expected) but would be happy with symmetrical diffs Guido prefers the (actual, expected) ordering but prefers diffs to show the other way round
Actually I said there was no right answer. I certainly do not want the diff output to treat the second arg as "old" and the first one as "new" -- that would be just as confusing. All in all I'd like to get rid of any vestiges of actual and expected; I think the first/second wording is the best we can come up with it.
R David Murray agreed with Guido
That's hard to believe since I don't agree with myself. :-)
Terry Reedy liked the change Glenn Linderman wants (actual, expected) and diffing to follow that Ron Adam ditto
Symmetrical diffs (element in first not in second, element in second not in first) solves the problem without imposing an order on the arguments. Actually unittest *has* used (first, second) to refer to the arguments to asserts pretty much since its inception. Losing the (actual, expected) terminology is a cost of this but unittest hasn't emphasised this terminology in the past (as I thought it had).
This won't work for diffing strings (assertMultiLineEqual) which use difflib and needs a direction for the diff. As above it is currently the wrong way round for (actual, expected).
The other alternative is to make them consistent and follow Nick's suggestion adding the header note to the diffs that old=expected, new=actual.
assertRaises() *is* an exception to the general actual/expected pattern, but that asymmetry is forced by the ability to pass arbitrary positional arguments to the function being tested (which later proved useful for the context manager form as well).
The (actual, expected) pattern matches the way almost everyone I've ever seen write if statements and asserts:
if x == 5: rather than if 5 == x: assert x == 5 rather than assert 5 == x
It also matches functions like isinstance and issubclass.
On the other hand it doesn't match the way we report TypeErrors where we report "expected <some type>, got <other type>".
All the best,
Michael Foord
Cheers, Nick.
--
May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
_______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/guido%40python.org
-- --Guido van Rossum (python.org/~guido)
On Tue, Dec 21, 2010 at 11:17 PM, Michael Foord
On 21/12/2010 01:57, Nick Coghlan wrote:
My own +1 goes to keeping the actual/expected terminology (and ordering) and adjusting the diffs accordingly (with a header noting that the diff is old=expected, new=actual).
Well we don't have consensus. Whatever we do we need to be consistent, and in the absence of an agreement about a change we should at least make all the behaviour and documentation consistent.
From this discussion and the discussion on the issue tracker:
Myself, Nick Coghlan and Ezio Melotti prefer (actual, expected) Raymond like (actual, expected) but would be happy with symmetrical diffs Guido prefers the (actual, expected) ordering but prefers diffs to show the other way round R David Murray agreed with Guido Terry Reedy liked the change Glenn Linderman wants (actual, expected) and diffing to follow that Ron Adam ditto
Symmetrical diffs (element in first not in second, element in second not in first) solves the problem without imposing an order on the arguments. Actually unittest *has* used (first, second) to refer to the arguments to asserts pretty much since its inception. Losing the (actual, expected) terminology is a cost of this but unittest hasn't emphasised this terminology in the past (as I thought it had).
I actually agree with Guido that anything we do is going to be suboptimal in some way. Encouraging the actual/expected ordering and updating the diff output so "expected=old" strikes me as least bad, but using the neutral first/second terminology and doing the diffs as "first=old" wouldn't be terrible (although I'm personally -0 on it because it encourages putting the expected value first in order to get the diffs the right way around when an error occurs). Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Tue, Dec 21, 2010 at 5:12 PM, Nick Coghlan
I actually agree with Guido that anything we do is going to be suboptimal in some way. Encouraging the actual/expected ordering and updating the diff output so "expected=old" strikes me as least bad, but using the neutral first/second terminology and doing the diffs as "first=old" wouldn't be terrible (although I'm personally -0 on it because it encourages putting the expected value first in order to get the diffs the right way around when an error occurs).
There are several problems with the actual/expected terminology. First of all, the arguments are primarily thought of as (and look) positional, not as having names (and there's nothing you can do about this in the docs -- people copy code without reading docs). Furthermore, Java's jUnit puts expected first (and makes this part of the culture/religion), so people coming from there will use that order and be freaked out if you were to swap them. And last, the order of diff arguments (old new) is also ingrained in the culture (which actually matches the expected/actual order in my mind). I think the least bad thing would be to drop any remnants of expected/actual terminology, keep the diffs in the first-second order, and let developers choose whether they put the expected value first or second. Then of course there will still be the examples in the doc (which some people *do* read and copy) -- I suppose we could alternate here to emphasize that we don't have a preferred order. -- --Guido van Rossum (python.org/~guido)
On 12/21/2010 7:17 AM, Michael Foord wrote: My first priority is that doc and code match. Close second is consistency (hence, ease of learning and use) between various AssertXs.
Symmetrical diffs (element in first not in second, element in second not in first) solves the problem without imposing an order on the arguments.
Where applicable, I prefer this as unambiguous output headings. -- Terry Jan Reedy
On 22 December 2010 01:37, Guido van Rossum
Furthermore, Java's jUnit puts expected first (and makes this part of the culture/religion), so people coming from there will use that order and be freaked out if you were to swap them. And last, the order of diff arguments (old new) is also ingrained in the culture (which actually matches the expected/actual order in my mind).
For what it's worth, none of the (numerous) Java projects that I've worked on over the last couple of years have used bare JUnit. Assertions are often done using the Hamcrest library - http://code.google.com/p/hamcrest/. The actual value comes first, and this combined with the use of matchers makes the failure messages very descriptive. I'd hate to go back to one of those projects where the tests tell you that two values don't match, but doesn’t tell me which is the expected value. There is a Python port - http://code.google.com/p/hamcrest/wiki/TutorialPython. I must get around to trying that. -- Cheers, Simon B.
On 22/12/2010 02:26, Terry Reedy wrote:
On 12/21/2010 7:17 AM, Michael Foord wrote:
My first priority is that doc and code match. Close second is consistency (hence, ease of learning and use) between various AssertXs.
Symmetrical diffs (element in first not in second, element in second not in first) solves the problem without imposing an order on the arguments.
Where applicable, I prefer this as unambiguous output headings.
Could you explain what you mean? All the best, Michael Foord -- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
On 12/24/2010 11:09 AM, Michael Foord wrote:
On 22/12/2010 02:26, Terry Reedy wrote:
On 12/21/2010 7:17 AM, Michael Foord wrote:
My first priority is that doc and code match. Close second is consistency (hence, ease of learning and use) between various AssertXs.
Symmetrical diffs (element in first not in second, element in second not in first) solves the problem without imposing an order on the arguments.
Where applicable, I prefer this as unambiguous output headings.
Could you explain what you mean?
I was referring back to an output example symmetric diff that was clipped somewhere along the way: In x not in y: ... In y not in x: ... rather than just using -,+ prefixes which are not necessarily self-explanatory. 'Not applicable' would refer to output from difflib which necessarily is ordered. -- Terry Jan Reedy
On Dec 24, 2010, at 10:56 AM, Terry Reedy wrote:
On 12/24/2010 11:09 AM, Michael Foord wrote:
On 22/12/2010 02:26, Terry Reedy wrote:
On 12/21/2010 7:17 AM, Michael Foord wrote:
My first priority is that doc and code match. Close second is consistency (hence, ease of learning and use) between various AssertXs.
Symmetrical diffs (element in first not in second, element in second not in first) solves the problem without imposing an order on the arguments.
Where applicable, I prefer this as unambiguous output headings.
Could you explain what you mean?
I was referring back to an output example symmetric diff that was clipped somewhere along the way:
In x not in y: ... In y not in x: ...
rather than just using -,+ prefixes which are not necessarily self-explanatory. 'Not applicable' would refer to output from difflib which necessarily is ordered.
FWIW, I think + and - prefixes are much better for diffs that some made-up verbiage. People are used to seeing diffs with + and -. Anything else will be so contrived that it's net effect will be to make the output confusing and hard to interpret. If you want, add two lines of explanation before the diff: + means "in x, not in y" - means "in y, not it x" The notion of "making symmetric" can easily get carried too far, which a corresponding loss of useability. You get 95% of the benefit from two small changes: * Change the parameter names from "actual" and "expected" to "first" and "second" * Change the words "unexpected" and "missing" to "in first, not in second" and "in second, not in first". We have a strong history in using +/- and shouldn't throw away its brevity and clarity. Raymond
On 12/24/2010 02:03 PM, Raymond Hettinger wrote:
On Dec 24, 2010, at 10:56 AM, Terry Reedy wrote:
On 12/24/2010 11:09 AM, Michael Foord wrote:
On 22/12/2010 02:26, Terry Reedy wrote:
On 12/21/2010 7:17 AM, Michael Foord wrote:
My first priority is that doc and code match. Close second is consistency (hence, ease of learning and use) between various AssertXs.
Symmetrical diffs (element in first not in second, element in second not in first) solves the problem without imposing an order on the arguments.
Where applicable, I prefer this as unambiguous output headings.
Could you explain what you mean?
I was referring back to an output example symmetric diff that was clipped somewhere along the way:
In x not in y: ... In y not in x: ...
rather than just using -,+ prefixes which are not necessarily self-explanatory. 'Not applicable' would refer to output from difflib which necessarily is ordered.
FWIW, I think + and - prefixes are much better for diffs that some made-up verbiage. People are used to seeing diffs with + and -. Anything else will be so contrived that it's net effect will be to make the output confusing and hard to interpret.
Agree.
If you want, add two lines of explanation before the diff: + means "in x, not in y" - means "in y, not it x"
The notion of "making symmetric" can easily get carried too far, which a corresponding loss of useability.
I agree with this also. I don't understand the effort to make the tests be symmetric when many of the tests are non-symmetric. (see list below) I think the terms expected and actual are fine and help more than they hurt. I think of these as "actual result" and "expected result". A clearer terminology might be "expr" and "expected_result". Where a tests can be used *as if* they are symmetric, but the diff context is reversed, I think that that is ok. It just needs a entry in the docs that says that will happen if you do it. That won't break tests already written. Also notice (in the list below) that the use of 'a' and 'b' do not indicate a test is symmetric, but instead are used where they are *not-symmetric*. First and second could be used for those, but I think 'a' and 'b' have less mental luggage when it comes to visually seeing the meaning of the method signature in those cases. Tests where the order is not important usually use numbered but like arguments, such as "expr1" and "expr2" or "list1" and "list2". This makes sense to me. "obj1" and "obj2" are just two objects. The terms "x in y" and "x not in y" look like what you should get from containment or regex asserts. I guess what I'm try to say is think of the whole picture when trying to make improvements like these, an idea that works for one or two things may not scale well. Cheers, Ron Non-symmetric assert methods. assertDictContainsSubset(self, expected, actual, msg=None) assertFalse(self, expr, msg=None) assertGreater(self, a, b, msg=None) assertGreaterEqual(self, a, b, msg=None) assertIn(self, member, container, msg=None) assertIsInstance(self, obj, cls, msg=None) assertIsNone(self, obj, msg=None) assertIsNotNone(self, obj, msg=None) assertLess(self, a, b, msg=None) assertLessEqual(self, a, b, msg=None) assertNotIn(self, member, container, msg=None) assertIsInstance(self, obj, cls, msg=None) assertIsNone(self, obj, msg=None) assertIsNotNone(self, obj, msg=None) assertNotIn(self, member, container, msg=None) assertNotIsInstance(self, obj, cls, msg=None) assertRegex(self, text, expected_regex, msg=None) assertNotRegexMatches(self, text, unexpected_regex, msg=None) assertRaises(self, excClass, callableObj=None, *args, **kwargs) assertRaisesRegex(self, expected_exception, expected_regex, callable_obj=None, *args, **kwargs) assertRegex(self, text, expected_regex, msg=None) assertTrue(self, expr, msg=None) assertWarns(self, expected_warning, callable_obj=None, *args, **kwargs) assertWarnsRegex(self, expected_warning, expected_regex, callable_obj=None, *args, **kwargs)
participants (11)
-
Antoine Pitrou
-
Glenn Linderman
-
Guido van Rossum
-
Michael Foord
-
Nick Coghlan
-
R. David Murray
-
Raymond Hettinger
-
Ron Adam
-
Simon Brunning
-
Steven D'Aprano
-
Terry Reedy