Author: Chi-chi Wang
Course: PluralSight
- Vocabulary
- First Test Case
- Test Runner
- Test Suite
- Skipping A Test Case
- setUp and tearDown
- Anatomy of a Test Case
A unit test checks the behavior of a system, elements of code, and behavior of code. An element of code refers to a method, a function, a module, or a class.
An automated test is designed by a person and runs without intervention, unambiguously reporting the results as pass or fail. An automated test is designed to be run by any developer who has access to your code.
By the strict definition of a unit test, a test is not a unit test if it uses any external resource, such as the filesystem, a database, or a network.
Other tests that utilize external resources may be referred to as integration tests, functional tests, or acceptance tests.
A test case tests a specific behavior of a system. Each test case should be able to be run independently of others as many times as necessary, and clearly report a pass or fail result. It should not contain side effects that other test cases will rely on. The name of a test case should be explanatory to readers of the test, providing clarity to what is being tested.
A simple example of a unit test with one test case in the exercises directory.
To run the example test case, navigate into the test directory and run the following command: python3.7 -m unittest
.
NOTE: Within a unittest
class, a test case method name must begin with test_
to be picked up by the test runner.
A test runner is a program that executes test cases and reports the results. In the unittest module the command line test runner is built-in: python3.7 -m unittest
. It will discover and run all of the unit tests in that directory and below.
A test case should be designed independently of how it is going to be run: the developer should not have to care which test runner is going to be used while designing the test case.
A new test case has been added to exercise 01 demonstrating the use of unittest.assertEqual:
def test_lookup_entry_by_name(self):
phonebook = Phonebook()
phonebook.add("Bob", "12345")
self.assertEqual("12345", phonebook.lookup("Bob"))
The first argument to assertEqual
is the expected result and the second argument is the actual result.
Another new test case added, test_missing_entry_raises_KeyError
demonstrates the use of unittest.assertRaises.
You can run a test case with the CLI in verbose mode by changing into the test directory and running python3.7 -m unittest -v
. The -v
flag tells Python's unit test runner to run in verbose mode.
This mode will list out all of the test cases being run:
test_create_phonebook (test_phonebook_01.PhonebookTest_01) ... ok
test_lookup_entry_by_name (test_phonebook_01.PhonebookTest_01) ... ok
test_missing_entry_raises_KeyError (test_phonebook_01.PhonebookTest_01) ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.001s
OK
To run a single test case, you can pass in the -q
flag to the CLI with the fully qualified name of the test case:
> python3.7 -m unittest -q test_phonebook_01.PhonebookTest_01.test_lookup_entry_by_name
A test suite is a number of test cases that are being executed together by a test runner. A test suite can be chosen from a number of test classes, which themselves contain test cases.
To skip a test case, decorate the test case with @unittest.skip()
:
@unittest.skip("Output message")
def test_test_case_to_skip(self):
pass
An example of a skipped test case can be found in exercise 02 under the test file test_phonebook_02.py.
Running this test in verbose mode via CLI, python3.7 -m unittest -v
, will display the skipped test in the output:
test_create_phonebook (test_phonebook_02.PhonebookTest_02) ... ok
test_empty_phonebook_is_consistent (test_phonebook_02.PhonebookTest_02) ... skipped 'WIP'
test_lookup_entry_by_name (test_phonebook_02.PhonebookTest_02) ... ok
test_missing_entry_raises_KeyError (test_phonebook_02.PhonebookTest_02) ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.001s
OK (skipped=1)
setUp and tearDown are methods called immediately before and after a test method is called, respectively.
These methods are used to set up and clean up test fixtures. A test fixture is a piece of code that can construct and configure the system of the test to get it ready to be tested, and to clean up afterwards. It allows for the separation of concerns so that the test cases can concentrate on specifying and checking a particular behavior, and not be cluttered with general setup details.
A test case generally has a name, and then four parts: arrange, act, assert, and cleanup.
The name of your test case is very important: it acts as a headline summarizing the behavior your test case is looking for. It is the first thing you see when a test case fails - the test case name should give you a good indication of which functionality is not working.
The parts of a test case are as follows: Arrange: Set up the object to be tested, as well as collaborators. This section of the test can be split up between the test fixture and the test case itself.
Act: Excercise functionality of the object. This should usually only be one line of code, because a unit test is only checking one behavior.
Assert: Make claims about the object and its collaborators. Often there is only one assertion needed, because you are only checking one behavior.
Cleanup: Release resources, restore to the original state. Usually happens in the tearDown
part of the test fixture.