Like it!

Join us on Facebook!

Like it!

Run painless test suites in Python with Unittest

A gentle introduction to aggregate tests that will be executed together.

Writing unit tests in Python is fairly easy, thanks to the well-known Unittest module. What I found less obvious was how to organize those tests properly and run them all together.

Let me begin with a little bit of theory on how that module works and what it expects from your code. I will be using Python 3 for the following tutorial, but everything will work fine also with previous versions.

Cases, suites, runners, fixtures

The Unittest module borrows four basic concepts from the unit testing philosophy.

A text fixture is a function that contains the preparation of your test environment. Here you usually initialize databases, create files, prepare stuff so that your tests can work properly. Fixtures are also used to clean up the scene once finished.

A test case is a class that represents an individual unit of testing. That's the place where you make sure your code works correctly. It contains fixtures and calls to assert methods to check for and report failures.

A test suite is just a bunch of test cases together.

A test runner is a script that takes care of running the test suite.

My app needs to be tested

Now suppose that you are writing a minimalistic game made of the following objects: Player, Scenario and Thing. You are following a test-driven development so you write tests along the actual code.

Each test is contained in a module (i.e. a Python file), so you would end up with three modules/files. Let's set up a barebone test case for the Player object: with Unittest it would look something like that:

# tests/player.py

import unittest

class TestPlayer(unittest.TestCase):

    def setUp(self):
        ...

    def test_run(self):
        ...

    def test_attack(self):
        ...

    def tearDown(self):
        ...

First of all import the unittest module. That was obvious. The class TestPlayer is the actual test case and follows a naming convention: Test[whatYouWantToTest]. It also extends the unittest.TestCase base object to work: a test case is always created by subclassing the parent class.

Then, each test case begins and ends with setUp() and tearDown(): those are fixtures. There you put the code that will be executed before and after each test method. They are not mandatory: you can just omit them if you don't need specific initializations or cleanups.

The "body" of the test case is composed of test methods: test_run and test_attack in the example above. It's the place where you check that your code is running properly, with the aid of the assert methods.

Individual test methods' name must start with the letters test_. That's another naming convention required by the test runner to know which methods are the actual tests. More on that in a couple of seconds.

Just rinse and repeat the procedure for each class of your game and you eventually end up with several test cases, one for each component. You can then run the test cases one by one by hand, but that would be totally annoying. It's now time to set up a nice test suite and let it work for you on its own.

Organize tests in a test suite

The basic idea here is to have a new Python file runner.py alongside your tests that contains our runner. It looks something like the following:

# tests/runner.py
import unittest

# import your test modules
import player
import scenario
import thing

# initialize the test suite
loader = unittest.TestLoader()
suite  = unittest.TestSuite()

# add tests to the test suite
suite.addTests(loader.loadTestsFromModule(player))
suite.addTests(loader.loadTestsFromModule(scenario))
suite.addTests(loader.loadTestsFromModule(thing))

# initialize a runner, pass it your suite and run it
runner = unittest.TextTestRunner(verbosity=3)
result = runner.run(suite)

First of all import your modules containing your tests (player, scenario and thing, remember?). Then initialize the suite and the loader by calling unittest.TestLoader() and unittest.TestSuite().

Add your tests to the test suite with suite.addTests(loader.loadTestsFromModule([your-module-here])), then initialize the test runner and fire it with runner.run(suite).

I also set the verbosity level of the test runner to 3: that's how much information you'll see in the console output.

Launch the script and all your tests will be executed nicely.

Sources

Python Official Documentation - Unit testing framework (link)
Python Testing - unittest introduction (link)
Wikipedia - Test fixture (link)
Voidspace - Introduction to unittest (link)

comments
o_rety on March 26, 2018 at 21:16
Great tutorial. I recommended it on StackOverflow here: https://stackoverflow.com/questions/1896918/running-unittest-with-typical-test-directory-structure?rq=1#comment86001894_2992477
Triangles on March 27, 2018 at 16:26
@o_rety many thanks for your love!
Rita on May 04, 2018 at 10:45
Can you please show me content present in player,thing modules?
Triangles on May 07, 2018 at 10:39
@Rita actually there's not much of interest in there. Player and Thing are just two hypothetical objects from a hypothetical game architecture. Player class manages things like attack(), run(), getLifePoints(), ... while Thing could be thought as a base class for objects, rooms, weapons, power-ups and so on.
husen on November 02, 2018 at 16:28
short and simple good explanation!!
loren on July 09, 2019 at 08:55
very good explained and very helpful. :-)
Larry on October 07, 2019 at 10:21
Short and sweet. Thanks for this!!
Manohar on November 04, 2019 at 19:57
How you will be running the runner.py ?
python -m unittest runner
or
python runner.py
Triangles on November 08, 2019 at 11:10
@Manohar runner.py is a regular script, so `python runner.py` should be enough (I suppose).
devi on November 29, 2019 at 12:57
very good tutorial
Ronak on June 05, 2020 at 14:27
Very Helpful!
Thank you!!
Siddharth on January 03, 2021 at 12:01
Thank You!
Peter on April 07, 2021 at 14:50
Very helpful, I had to search for a solution for a while so I am glad I've your tutorial
apb on September 30, 2021 at 17:58
Thanks a lot, very useful example !
Yomi Longe (olonge) on August 20, 2022 at 00:16
After searing Bing/google for almost an hour, this is by far the best intro to unittest suites I could find.

Simple, clear, concise and immensely useful.
Many thanks!
Рома on June 30, 2023 at 09:16
Спасибо большое! Во всех источниках набор собирался из функции addTest, что неудобно.