To assert or assertEqual in Python unit testing

14 February 2009   16 comments   Python

Mind That Age!

This blog post is 8 years old! Most likely, it's content is outdated. Especially if it's technical.

Powered by Fusion×

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
   func(para, meter)
   raise Exception
except exception:

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:


into this:

assert not errors

Isn't that just nicer to use/read/write?


Victor Noagbodji
I 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...
Peter Bengtsson
No, the summary is the same me thinks. Take this example code:

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)
Chris McDonough
"assert" statements aren't run when Python is run in "-O" (optimize) mode. That's about the only reason to not do this.
Michal Bartoszkiewicz
assertEquals generates nicer error message when it fails – AssertionError: 'foo' != 'bar' instead of just AssertionError.
Marius Gedminas
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.
Floris Bruynooghe
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.
Peter Bengtsson
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.
Doug Latornell
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")
Peter Bengtsson
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.
Dan Lepage
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.
Gary Godfrey
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

Gary Godfrey
Austin, TX, USA
Peter Bengtsson
I've never seen it used in any of the big ones. eval() is very rarely used.
Brian Beck
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(, "bar")
Peter Bengtsson
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(, "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.
Brian Beck
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. :)
Thank you for posting a comment

Your email will never ever be published

Related posts

Female body builder picture galore 12 February 2009
Founder of Islamic TV station accused of beheading wife 17 February 2009
set -ex - The most useful bash trick of the year 31 August 2014
Careful with your assertRaises() and inheritance of exceptions 10 April 2013
Eloquent Javascript by Marijn Haverbeke 25 February 2011
EditDistanceMatcher - NodeJS script for doing edit distance 1 matching 05 February 2011
Speed of DoneCal API (over 1,400 request/sec) and HTTPS (less than 100 request/sec) 27 December 2010
My tricks for using AsyncHTTPClient in Tornado 13 October 2010
Correction: running Django tests with MongoDB is NOT slow 30 May 2010
Review: Django 1.1 Testing and Debugging 20 May 2010
Mocking os.stat in Python 08 November 2009 - Using (py)inotify to run commands when files change 20 July 2009