Lets start with a trivial implementation file greeter.py:
class Greeter(object):
def __init__(self, name):
self.name = name
def greet(self):
return 'Hi, {}!'.format(self.name)Unittest is the standard library testing package that comes with Python. It is based on jUnit (Java-ness).
Here is a basic test file for greeter tests/test_greeter.py. The test_ prefix is important for unittest. It can automatically discover your tests, but it looks for files with this.
import unittest
import greeter
class GreeterTest(unittest.TestCase):
def setUp(self):
self.gr = greeter.Greeter('Jim-bob')
def test_greet(self):
self.assertEqual(self.gr.greet(), 'Hi, Jim-bob!')
def test_name(self):
self.assertEqual(self.gr.name, 'Jim-bob')Then, from the root of the working directory, you can run the tests with:
$ python -m unittest testsYou can run a single test file with:
$ python -m unittest tests/test_greeter.pyTesting a single test can be done with something like the following, but this didn't work for me:
$ python -m unittest tests/test_greeter.py tests.test_greeter.GreeterTest.test_greeter- The API is very long winded, like jUnit
- The output is not colorized, showing red and green
- The verbose mode of the output (passing -v flag to command) is fairly useless
- The test file need to be prefixed with
test_, but in most other languages a postifix is more standard (e.g._test.pywould be better) - Testing a single test syntax is longwinded
Pytest solves many of the problems with unittest.
It simplifies the API. Here is a similar test with pytest tests/greeter_test.py. Notice the test can be postfixed with _test.py instead of prefixed.
import pytest
import greeter
@pytest.fixture
def gr():
return greeter.Greeter('Jim-bob')
def test_greet(gr):
assert gr.greet() == 'Hi, Jim-bob!'
def test_name(gr):
assert gr.name == 'Jim-bob'You can run the tests in the tests directory with. Notice the output is colorized for red/green TDD flow.
$ pytestYou can run a specific file with:
$ pytest tests/greeter_test.pyYou can run in verbose mode to see which tests were run:
$ pytest -vYou can run a single test with
$ pytest -v tests/greeter_test.py::test_greetNotice how much easier it is to call out an individual test than it is with unittest.
If you are seeing an error when trying to import your implementation module that is up one directory, you may need to add an __init__.py file to the root of your tests directory.
Pytest captures output by default, but the -s option turns capturing off, which allows you to drop an iPython debug statement in your code:
import pytest
import greeter
from IPython.core import debugger
debug = debugger.Pdb().set_trace
@pytest.fixture
def gr():
return greeter.Greeter('Jim-bob')
def test_greet(gr):
debug()
assert gr.greet() == 'Hi, Jim-bob!'
def test_name(gr):
assert gr.name == 'Jim-bob$ pytest -s -v tests/greeter_test.py::test_greet