Search code examples
pythonunit-testingpytestassertnose

Why not use python's assert statement in tests, these days?


In Python testing, why would you use assert methods:

self.assertEqual(response.status_code, 200)
self.assertIn('key', my_dict)
self.assertIsNotNone(thing)

As opposed to the direct assertions:

assert response.status_code == 200
assert 'key' in my_dict
assert thing is not None

According to the docs:

These methods are used instead of the assert statement so the test runner can accumulate all test results and produce a report

However this seems to be bogus, a test runner can accumulate results and produce a report regardless. In a related post unutbu has shown that unittest will raise an AssertionError just the same as the assert statement will, and that was over 7 years ago so it's not a shiny new feature either.

With a modern test runner such as pytest, the failure messages generated by the assertion helper methods aren't any more readable (arguably the camelCase style of unittest is less readable). So, why not just use assert statements in your tests? What are the perceived disadvantages and why haven't important projects such as CPython moved away from unittest yet?


Solution

  • I'm not entirely sure I understand the question. The title is "Why not use pythons assert statement in tests these days".

    As you've noted, in fact you can use plain assertions if you use a test-framework like pytest. However pytest does something quite special to get this to work. It re-writes the plain assertions in the test-code before it runs the tests.

    See https://docs.pytest.org/en/stable/writing_plugins.html#assertion-rewriting which states:

    One of the main features of pytest is the use of plain assert statements and the detailed introspection of expressions upon assertion failures. This is provided by “assertion rewriting” which modifies the parsed AST before it gets compiled to bytecode.

    The unittest framework does not implement this extra complexity. (And it is extra complexity. Pytest re-writes only the assertions in the test cases, it will not re-write the assertions in the other python library your test-code uses. So you will sometimes find pytest hits an assertion error in your test-code, but there's no detail about why the assertion has failed, because it hasn't re-written that bit of your code. And thus you only get a plain AssertionError without any information as to why it failed.)

    Instead, unittest provides methods like assertEqual so that it can:

    1. Know it's a test assertion that has failed rather than some other unhandled/unexpected exception; and
    2. It can provide information as to why the assertion is not satisfied. (A plain assertion in python does nothing but raise AssertionError. It does not say, for example AssertionError because 1 != 2)

    Pytest does number 1 and 2 by re-writing the Abstract Syntax Tree before it runs the test-code. Unittest takes the more traditional approach of asking the developer to use particular methods.

    So essentially the answer is: it's an implementation difference between the test-frameworks. Put another way, Python's in-built assert statement provides no debugging information about why the failure occurred. So, if you want some more information, you need to decide how you're going to implement it.

    Unittest is a lot simpler than pytest. Pytest is great but it is also a lot more complicated.