Introduction: Test Cases¶
A test case expresses requirements for a program, in a way that can be checked automatically. Specifically, a test asserts something about the state of the program at a particular point in its execution.
We have previously suggested that it’s a good idea to first write down comments about what your code is supposed to do, before actually writing the code. It is an even better idea to write down some test cases before writing a program. For example, before writing a function, write a few test cases that check that it returns an object of the right type and that it returns the correct values when invoked on particular inputs.
You’ve actually been using test cases throughout this book in some of the activecode windows and almost all of the exercises. The code for them has been hidden, so as not to confuse you and also to avoid giving away the answers. Now it’s time to learn how to write code for test cases.
Python provides a built-in module for writing and running test cases, called unittest
. As you write larger programs, as part of larger software development teams, you will learn a lot more details about how to structure collections of test cases and learn a discipline for where to collect them and when to run them. This course introduces just a few features of the unittest
module.
As with other modules, the first step is to write a statement to import it. Having done that, you can refer to the classes and functions that are defined in the unittest module.
The unittest module defines a class called TestCase
. Rather than directly creating instances of this class, the normal way to use this class is to create a subclass of it. Within the subclass, you define one or more methods. By convention, the name of each method should begin with the word test_
. This helps you, and other programmers, keep track of what’s going on in your code. Each method that you define in the subclass of TestCase
is one test case.
The code defining test case methods can make “assertions”, by invoking one of the assertion methods that is defined for the TestCase class. For example, we can write self.assertEqual(x, 3). If the current value of the variable x is 3, the assertion is valid and the test will pass. If it’s not, then the test will fail.
The unittest module uses classes in a way that’s a little bit non-intuitive. You will never write code that explicitly invokes your subclass of TestCase to make an instance of it. Instead, that will happen behind the scenes in a function called main()
that is defined in the unittest module. It will search for all subclasses of TestCase
. With each subclass, it searches for every method of that class that begins with the prfeix test
. With each method, it then creates an instance of the class and calls the method to run the tests.
What you will need to learn is how to import the unittest framework, which you learned about in the modules chapter, and write code to:
- Make a subclass of
unittest.TestCase
. - Define methods for test cases.
- Use assertion methods inside the methods that you define to test specifics about your code.
For example, the code snippet below, illustrates a set of tests for string methods. It is a simplified version of the code provided in the python documentation for the unittest module. The full unittest module can do a lot more than we’ve shown here, including automatically running a setUp method before running each test in a TestCase subclass.
import unittest
class TestStringMethods(unittest.TestCase): # make a sublcass
def test_upper(self): # define a method
self.assertEqual('foo'.upper(), 'FOO') # use an assert method to test if something is true
def test_isupper(self):
self.assertTrue('FOO'.isupper())
self.assertFalse('Foo'.isupper())
def test_split(self): # any name ok
# but note the convention that it should start with the word test
s = 'hello world'
self.assertEqual(s.split(), ['hello', 'world'])
# invoke the main() function from the unittest module, which runs all the tests
unittest.main(verbosity=2)
In the online textbook, we use a special module that is built on top of the unittest module. This one, however, handles making a nice tabular display of the results of the tests, and putting them into HTML form to display on the web page. To use it, we import the module unittest.gui
rather than just unittest, and then we work with the TestCaseGui
class rather than the TestCase
class. TestCaseGui includes a main() method that does pretty much what the unittest.main function does. In your own code files, you will use the TestCase
class, and get output in your console that does not look quite as neat as the tables you’ve seen in this textbook.
Here’s an example with test cases for the blanked function that you created in the Hangman problem set. Note that the tests will fail until you fill in a correct definition for the blanked function.
Check your understanding
-
rec-5-1: When
- True
- A message is printed out, but the program does not stop executing
- False
- A message is printed out, but the program does not stop executing
- It depends
- A message is printed out, but the program does not stop executing
TestCase.assertEqual()
is passed two values that are not the same, it generates an error and stops execution of the program.
-
rec-5-2: Test cases are a waste of time, because python interpreter will give an error
message when the program runs incorrectly.
- True
- You might not notice the error, if the code just produces a wrong output rather generating an error. And it may be difficult to figure out the original cause of an error when you do get one.
- False
- Test cases let you test some pieces of code as you write them, rather than waiting for problems to show themselves later.