How do I handle multiple asserts within a single Python unittest?
With using a subtest, execution would not stop after the first failurehttps://docs.python.org/3/library/unittest.html#subtests
Here is example with two fail asserts:
class TestMultipleAsserts(unittest.TestCase): def test_multipleasserts(self): with self.subTest(): self.assertEqual(1, 0) with self.subTest(): self.assertEqual(2, 0)
Output will be:
======================================================================FAIL: test_multipleasserts (__main__.TestMultipleAsserts) (<subtest>)----------------------------------------------------------------------Traceback (most recent call last): File "./test.py", line 9, in test_multipleasserts self.assertEqual(1, 0)AssertionError: 1 != 0======================================================================FAIL: test_multipleasserts (__main__.TestMultipleAsserts) (<subtest>)----------------------------------------------------------------------Traceback (most recent call last): File "./test.py", line 11, in test_multipleasserts self.assertEqual(2, 0)AssertionError: 2 != 0----------------------------------------------------------------------Ran 1 test in 0.000sFAILED (failures=2)
You can easy wrap subtest as following
class MyTestCase(unittest.TestCase): def expectEqual(self, first, second, msg=None): with self.subTest(): self.assertEqual(first, second, msg)class TestMA(MyTestCase): def test_ma(self): self.expectEqual(3, 0) self.expectEqual(4, 0)
I disagree with the dominant opinion that one should write a test method for each assertion. There are situations where you want to check multiple things in one test method. Here is my answer for how to do it:
# Works with unittest in Python 2.7class ExpectingTestCase(unittest.TestCase): def run(self, result=None): self._result = result self._num_expectations = 0 super(ExpectingTestCase, self).run(result) def _fail(self, failure): try: raise failure except failure.__class__: self._result.addFailure(self, sys.exc_info()) def expect_true(self, a, msg): if not a: self._fail(self.failureException(msg)) self._num_expectations += 1 def expect_equal(self, a, b, msg=''): if a != b: msg = '({}) Expected {} to equal {}. '.format(self._num_expectations, a, b) + msg self._fail(self.failureException(msg)) self._num_expectations += 1
And here are some situations where I think it's useful and not risky:
1) When you want to test code for different sets of data. Here we have an add() function and I want to test it with a few example inputs. To write 3 test methods for the 3 data sets means repeating yourself which is bad. Especially if the call was more elaborate.:
class MyTest(ExpectingTestCase): def test_multiple_inputs(self): for a, b, expect in ([1,1,2], [0,0,0], [2,2,4]): self.expect_equal(expect, add(a,b), 'inputs: {} {}'.format(a,b))
2) When you want to check multiple outputs of a function. I want to check each output but I don't want a first failure to mask out the other two.
class MyTest(ExpectingTestCase): def test_things_with_no_side_effects(self): a, b, c = myfunc() self.expect_equal('first value', a) self.expect_equal('second value', b) self.expect_equal('third value', c)
3) Testing things with heavy setup costs. Tests must run quickly or people stop using them. Some tests require a db or network connection that takes a second which would really slow down your test. If you are testing the db connection itself, then you probably need to take the speed hit. But if you are testing something unrelated, we want to do the slow setup once for a whole set of checks.
This feels like over-engineering to me. Either:
Use two asserts in one test case. If the first assert fails, it's true, you won't know whether the second assert passed or not. But you're going to fix the code anyway, so fix it, and then you'll find out if the second assert passed.
Write two tests, one to check each condition. If you fear duplicated code in the tests, put the bulk of the code in a helper method that you call from the tests.