You have already learned to check your work with
First, if an assertion fails, the program crashes immediately and you cannot see what would happen next.
Second, there is no summary: did 5 tests pass? Did one fail?
The standard library provides
assert statements. That is a good start, but it has two problems.First, if an assertion fails, the program crashes immediately and you cannot see what would happen next.
Second, there is no summary: did 5 tests pass? Did one fail?
The standard library provides
unittest, which solves both issues. It runs a collection of test methods, collects every failure, and gives you a clean report at the end.Writing unittest tests
import unittest
def add(a: int, b: int) -> int:
return a + b
class AddTests(unittest.TestCase):
def test_basic(self):
self.assertEqual(add(2, 3), 5)
def test_negative(self):
self.assertTrue(add(-1, -1) == -2)Three rules matter here:
- The class must inherit from
unittest.TestCase. - Every test method's name must start with the prefix test (e.g.
test_basic). - You call assertion methods on
self:assertEqual,assertTrue,assertFalse,assertIn, and others.
import unittest
def divide(a: float, b: float) -> float:
if b == 0:
raise ValueError('division by zero')
return a / b
class DivideTests(unittest.TestCase):
def test_zero_raises(self):
with self.assertRaises(ValueError):
divide(1, 0)assertRaises works as a context manager. The code inside the with block must raise that type of exception, or a subclass of it. If nothing is raised, the test fails. If a different exception is raised, it escapes the with block and unittest records the test as an error.Running tests in this course
import unittest
import io
def add(a: int, b: int) -> int:
return a + b
class AddTests(unittest.TestCase):
def test_basic(self):
self.assertEqual(add(2, 3), 5)
suite = unittest.defaultTestLoader.loadTestsFromTestCase(AddTests)
result = unittest.TextTestRunner(stream=io.StringIO()).run(suite)
print(result.wasSuccessful()) # True or False
print(result.testsRun) # how many tests ranresult.failures is a list of (test_case, traceback_string) tuples. len(result.failures) tells you how many tests failed an assertion, and each traceback string tells you why. A test that crashes with some other exception is listed in result.errors instead, so result.wasSuccessful() is the overall answer.setUp: shared fixtures
import unittest
class CartTests(unittest.TestCase):
def setUp(self) -> None:
self.items = ['apple', 'banana']
def test_has_apple(self):
self.assertIn('apple', self.items)
def test_length(self):
self.assertEqual(len(self.items), 2)setUp is called automatically right before each test_* method. Use it to build the state that several tests share, so you do not repeat boilerplate.doctest: examples as documentation
def square(n: int) -> int:
"""
>>> square(3)
9
>>> square(-2)
4
"""
return n * nEach
>>> line is an expression; the lines straight after it, at the same indent as the >>>, are its expected output. To check them, call:import doctest
doctest.run_docstring_examples(square, globals())