unittest for the graph paper function

graphpaper.py (the module)
graphpaperunittest.py

To put the graph paper code into testable form, I packaged it as the function draw in line 7 of graphpaper.py. This function takes four int arguments and returns a string containing the graph paper. Run graphpaper.py by itself to try out the function by passing it arguments and printing its return value.

Class TestGraphpaper in line 11 of graphpaperunittest.py is a subclass of class unittest.TestCase. That means that class TestGraphpaper has all the attributes and methods of class unittest.TestCase, plus more. We say that class TestGraphpaper inherits all the attributes and methods of its superclass unittest.TestCase.

Here are two examples. It looks like class TestGraphpaper has only the three methods in lines 13, 24, and 33. But this class also has methods named assertRaises (lines 19 and 38) and assertEqual (line 45), which class TestGraphpaper inherits from class unittest.TestCase.

The methods of class TestGraphpaper whose names begin with test_ (lines 13, 24, and 33) are called automatically by the unittest.main function in line 52. The test_ methods are executed in alphabetical order. To get a different order, we could have given them names like
test_001_return
test_002_type
test_003_range

The list comprehension in line 29 creates the list [1, 1, 1, 1]. The first time line 30 is executed, it changes this list to [0, 1, 1, 1]. These four ints are then passed to the draw function as four separate arguments using the asterisk we saw in line 23 of variablenumber.py in Variable number. The 0 is an illegal argument for draw and should make this function raise the ValueError exception.

The with in line 28 makes the computer behave as if we had surrounded lines 29–31 with try and except. In other words, the with will prevent any exception raised by lines 29–31 from terminating the script. On the other hand, lines 29–31 (specifically, line 31) are supposed to raise a ValueError exception because of the bad argument 0. If these lines do not raise the exception, the assertRaises method in line 28 will report an error in the final output of the script.

The lines under the with (lines 29–31) must not contain any lines after the line that raises the exception (line 31). Any line after the line that raises the exception would never be executed.

I wanted to include True and False in the tuple of arguments of illegal (i.e., non-integer) data types in line 16. But it turned out that True and False were actually integers in disguise, so they were legal.

Run the script graphpaperunittest.py

Save graphpaper.py and graphpaperunittest.py in the directory that holds all your other Python scripts. Open graphpaperunittest.py in an IDLE window (by pulling down File → Open…) and run it (by pulling down Run → Run Module).

Or open the Terminal window (on Microsoft, the Command Prompt window), go to your Python directory, and say the following. (On Microsoft, say py.exe instead of python3.)

python3 graphpaperunittest.py

In either case, the output will be the following. The time is in seconds; running from the Terminal is faster than running from IDLE.

test_range (__main__.TestGraphpaper)
Does draw raise a ValueError an argument is a non-positive integer? ... ok
test_return (__main__.TestGraphpaper)
Does draw return the correct value? ... ok
test_type (__main__.TestGraphpaper)
Does draw raise a TypeError if an argument is a non-integer? ... ok

----------------------------------------------------------------------
Ran 3 tests in 0.002s

OK

If you have launched graphpaperunittest.py from the terminal window, you can check the exit status of this script immediately after running it by saying

echo $?
0

Things to try

  1. Temporarily sabotage the draw function in graphpaper.py. For example, change the "+" to "*" in line 42. The error will be discovered when the test_return method calls draw for the first time, passing it the arguments 1, 1, 1, 1. The hieroglyphics tell us that we’d have to remove "+-" from the left string and add "*-" to the left string to make the two strings equal.
    test_range (__main__.TestGraphpaper)
    Does draw raise a ValueError an argument is a non-positive integer? ... ok
    test_return (__main__.TestGraphpaper)
    Does draw return the correct value? ... FAIL
    test_type (__main__.TestGraphpaper)
    Does draw raise a TypeError if an argument is a non-integer? ... ok
    
    ======================================================================
    FAIL: test_return (__main__.TestGraphpaper)
    Does draw return the correct value?
    ----------------------------------------------------------------------
    Traceback (most recent call last):
      File "graphpaperunittest.py", line 47, in test_return
        rowsOfSpaces, columnsOfSpaces
    AssertionError: '+-\n| \n' != '*-\n| \n'
    - +-
    + *-
      |
    
    
    ----------------------------------------------------------------------
    Ran 3 tests in 0.001s
    
    FAILED (failures=1)
    
    echo $?
    1