Long Refactoring: Testing

The code runs. How do me make sure that continues to be the case? Write a test.

The output from the previous post shows that the code runs. The ideal functional test would be to snapshot this and compare with the snapshot. If it is identical, we haven’t borked up nuffin.

To start with, let’s create a new file called test_tree_soduku.py. Populate it with this old chestnut:

print ("Hello World")
assert(False)

Make sure we can run it.

$ python3 test_tree_soduku.py
Hello World
Traceback (most recent call last):
  File "test_tree_soduku.py", line 2, in <module>
    assert(False)
AssertionError

I always want to see out put. I want to make sure I am not fooling myself. The assert(False) is what I put into every test before I run it, so I don’t accidentally skip running a test.

Let’s make this a little more functional. For now, use the python module for running another program. https://docs.python.org/3/library/subprocess.html

import subprocess
print ("Running Test")
p = subprocess.run(["python3", "./tree_sudoku.py"], capture_output=True)
print(p.stdout)
assert(False)

Here is the output.

$ python3 test_tree_soduku.py
Hello World
b'Board: 0\n---------------------\n4 8 3 | 9 2 1 | 6 5 7 \n9 6 7 | 3 4 5 | 8 2 1 \n2 5 1 | 8 7 6 | 4 9 3 \n---------------------\n5 4 8 | 1 3 2 | 9 7 6 \n7 2 9 | 5 6 4 | 1 3 8 \n1 3 6 | 7 9 8 | 2 4 5 \n---------------------\n3 7 2 | 6 8 9 | 5 1 4 \n8 1 4 | 2 5 3 | 7 6 9 \n6 9 5 | 4 1 7 | 3 8 2 \n---------------------\nBoard: 1\n---------------------\n2 4 5 | 9 8 1 | 3 7 6 \n1 6 9 | 2 7 3 | 5 8 4 \n8 3 7 | 5 6 4 | 2 1 9 \n---------------------\n9 7 6 | 1 2 5 | 4 3 8 \n5 1 3 | 4 9 8 | 6 2 7 \n4 8 2 | 7 3 6 | 9 5 1 \n---------------------\n3 9 1 | 6 5 7 | 8 4 2 \n7 2 8 | 3 4 9 | 1 6 5 \n6 5 4 | 8 1 2 | 7 9 3 \n---------------------\nBoard: 2\n---------------------\n4 6 2 | 8 3 1 | 9 5 7 \n7 9 5 | 4 2 6 | 1 8 3 \n3 8 1 | 7 9 5 | 4 2 6 \n---------------------\n1 7 3 | 9 8 4 | 2 6 5 \n6 5 9 | 3 1 2 | 7 4 8 \n2 4 8 | 5 6 7 | 3 1 9 \n---------------------\n9 2 6 | 1 7 8 | 5 3 4 \n8 3 4 | 2 5 9 | 6 7 1 \n5 1 7 | 6 4 3 | 8 9 2 \n---------------------\nFinished in 4.880897283554077 seconds\n'
Traceback (most recent call last):
  File "test_tree_soduku.py", line 9, in <module>
    assert(False)
AssertionError

Now let’s convert this into an automated test. We’ll capture the output in a string and compare it. Note that the letter “b” at the start of the output tells us this is bytes, not a string. Converting the output to a string will give us more ability to morph it. Let’s start there.

import subprocess
print ("Running Test")
p = subprocess.run(["python3", "./tree_sudoku.py"], capture_output=True)
 
print(type(p.stdout))
output = p.stdout.decode("utf-8")
print(type(output))
#assert(False
$ python3 test_tree_soduku.py
Running Test
<class 'bytes'>
<class 'str'>

Since we want to avoid changing the original code, we’ll instead strip out anything from the output we don’t want. Lets get rid of all of the formatting characters first. A little trial and error gives me this code:

import subprocess
print ("Running Test")
p = subprocess.run(["python3", "./tree_sudoku.py"], capture_output=True)
 
output = p.stdout.decode("utf-8").split("\n")
output = "".join(output[:-2])
output = output.replace("-","").replace("|","")
output = output.replace(" ","").replace("\n","")
output = output.replace("Board","\nBoard")
print(output)
 
#assert(False)
$ python3 test_tree_soduku.py
Running Test
 
Board:0483921657967345821251876493548132976729564138136798245372689514814253769695417382
Board:1245981376169273584837564219976125438513498627482736951391657842728349165654812793
Board:2462831957795426183381795426173984265659312748248567319926178534834259671517643892

I removed the last line of output (last two actually) as the time changes each time I run the program.

This output is a little horrible, in that it appends a number to the front of the solution: 1, 2,3. However, this is not our end state, and it is good enough for our purposes. I could do regex matches to get rid of other stuff, but I don’t want to spend more time than necessary here. I just want the output to be short enough to fit in code.

Let’s codify that output into our test.

import subprocess
check_data ="""
Board:0483921657967345821251876493548132976729564138136798245372689514814253769695417382
Board:1245981376169273584837564219976125438513498627482736951391657842728349165654812793
Board:2462831957795426183381795426173984265659312748248567319926178534834259671517643892"""
print ("Running Test")
p = subprocess.run(["python3", "./tree_sudoku.py"], capture_output=True)
output = p.stdout.decode("utf-8").split("\n")
output = "".join(output[:-2])
output = output.replace("-","").replace("|","")
output = output.replace(" ","").replace("\n","")
output = output.replace("Board","\nBoard")
print("comparing output ")
assert(len(check_data) == len(output))
assert(check_data == output)
print("OK")

I had originally had a problem when I put the final “”” on its own line, as it added an additional newline. I checked the length with:

 print("comparing output %d %d " % (len(check_data), len(output)))

Which is why that code is still in there.

OK, we have a test. Now we can start hacking on the original code.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.