When you write unit tests in Python you can use these widgets:
self.assertEqual(var1, var2, msg=None)
self.assertNotEqual(var1, var2, msg=None)
self.assertTrue(expr, msg=None)
self.assertRaises(exception, func, para, meters, ...)
That's fine but is it "pythonic" enough? The alternative is to do with with "pure python". Eg:
assert var1 == var2, msg
assert var1 != var2, msg
assert expr, msg
try:
func(para, meter)
raise Exception
except exception:
pass
I'm sure there are several benefits with using the unittest
methods that I don't understand but I understand the benefits of brevity and readability. The more tests you write the more tedious it becomes to write self.assertEquals(..., ...)
every time. In my own code I prefer to use simple assert
statements rather than the verbose unittest
alternative. Partially because I'm lazy and partially because they read better and the word assert
is highlit in red in my editor so it just looks nicer from a distance.
Perhaps some much more clever people than me can explain what a cardinal sin it is to not use the unittest
methods over the lazy more pythonic ones.
Incidentally, during the course of jotting down this blog I reviewed some old inherited code and changed this:
self.assertEqual(len(errors),0)
into this:
assert not errors
Isn't that just nicer to use/read/write?
Comments
Post your own commentI think one of the benefit of using the module methods are the analysis you can get at the end of tests. How many passed, failed, etc...
No, the summary is the same me thinks. Take this example code:
http://docs.python.org/library/unittest.html#basic-example
Fiddle it so that the assertEquals on line 13 fails (e.g. change range(10) to range(11)) and run it and you'll get this result:
----------------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (failures=1)
Now, edit it again and replace the self.assertEquals with a normal assert and run it again and you'll get this result:
----------------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (failures=1)
"assert" statements aren't run when Python is run in "-O" (optimize) mode. That's about the only reason to not do this.
This is the real reason why, good point.
assertEquals generates nicer error message when it fails AssertionError: 'foo' != 'bar' instead of just AssertionError.
What Chris said, with the additional argument that the tests should be run with the same Python executable and options as real code, so if you use -O to run your app, you should do it to run your tests.
(Personally, I'm not convinced, and I've never used -O to run production applications.)
Also, when you do
assert actual_result == expected_result
you get no information about what the actual result was, while self.assertEqual() prints both arguments.
For completeness' sake I have to mention that there are test runners (py.test and, I think, nose with some option turned on) that do magic to display this even when you use an assert statement.
Both nose and py.test encourage just using "assert" because it is more pythonic. However the nice thing of self.assertEqual() and friends is that you don't have to write the message yourself to get a nice message in case the test fails. That's why both nose and py.test do try to show the values of the objects involved in the assert statment, relieving you in most cases of having to write the message yourself.
People behind nose and py.test are clever cookies but unittest (which ZopeTestCase and Django TestClient is based on) is not nose or py.test.
Besides instead of::
assert actual_result == expected_result
I think it's more sensible to write::
assert actual_result == expected_result, expected_result
but already that's excessive typing.
In general I get the feeling that assertEquals() and its companions ARE more useful in terms of output but I still stand by my opinion that writing them is disadvantageous in favor of assert.
If that test fails, you have no idea what the actual_result was, since you're only shown what it wasn't.
assertEquals( x, y ) will tell you "Foo" != "Bar", your test will simply say, "failed: Bar". Why did it fail? Being told why it failed usually saves me a minute or two, so I can squeeze more productive work into the time I have available.
As others have mentioned, the convenience of self.assertEqual() generating a nice, useful message is a strong reason for using self.assertEqual() in my mind. However, the real deal-breaker for pure Python asserts is what happens if you use parentheses in an assert statement:
Python 2.5.1 (r251:54863, Jul 23 2008, 11:00:16)
[GCC 4.0.1 (Apple Inc. build 5465)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> assert False, "a short message"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError: a short message
>>> assert (False,
... "a long message that needs more than 1 line")
>>>
I think a short message is very pythonic. If you need to write something long you should reconsider the construct all together. Besides, it's just a string so you can write it on multiple lines with the \ backslash.
You've misplaced your parentheses:
>>> assert False, ('This is '
... 'a long message that requires me to use '
... 'several lines to write it down.')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError: This is a long message that requires me to use several lines to write it down.
Just curious - do any packages do something like:
import sys
def myass(s):
f0 = sys._getframe(0)
assert eval(s, f0.f_globals, f0.f_locals), "myass failed: '%s'" % s
a = 10
myass('a==10')
myass('a==20')
?
Gary Godfrey
Austin, TX, USA
I've never seen it used in any of the big ones. eval() is very rarely used.
I like the more helpful message output by assertEquals, but prefer using plain assert mostly because it's a keyword and stands out. So for a few cases I use helpers like the following (this is simplified, pretend assertEquals is unittest's):
def equals(a, b):
assertEquals(a, b)
return True
def test_foo():
assert equals(x.foo, "bar")
You say it's simplified. Fine. If you didn't simplify wouldn't that then become:
def equals(self, a, b):
self.assertEquals(a, b)
return True
def test_foo(self):
assert self.equals(x.foo, "bar")
That looks pretty verbose to me. It doesn't make it any neater or simpler.
We have to recognize a benefit with using standard self.assertEquals() that it's kind of a "standard" in that every programmer uses the same. If you then have to debug someone elses code and that uses the non-standard equals() it becomes harder to read.
No, equals is a global test helper, not a method. The tests don't need to be unittest.TestCase instances; it merely reuses unittest's assertEquals because it already exists.
assertEquals is only "standard" if you're using unittest; beyond that, you're going to have to become familiar with the codebase's test helpers anyway -- try navigating through Django's or SQLAlchemy's tests. :)