Not Yet Answered # Python unit testing framework

Gribouillis 1,313 Gribouillis 1,313 Discussion Starter ganil123

0

There is a nice article here http://www.onlamp.com/pub/a/python/2004/12/02/tdd_pyunit.html about the `unittest`

module, with examples. Personnaly, I prefer to use `doctest`

for test driven development. I'll try to give an example later with doctest.

0

Here is the example I promised about using `doctest`

for test driven development in python. I will show how you can use it to help you solve a small problem I found in project Euler (http://projecteuler.net/index.php?section=problems&id=158). Here is the problem

```
Taking three different letters from the 26 letters of the alphabet, character strings of length three can be formed.
Examples are 'abc', 'hat' and 'zyx'.
When we study these three examples we see that for 'abc' two characters come lexicographically after its neighbour to the left.
For 'hat' there is exactly one character that comes lexicographically after its neighbour to the left. For 'zyx' there are zero characters that come lexicographically after its neighbour to the left.
In all there are 10400 strings of length 3 for which exactly one character comes lexicographically after its neighbour to the left.
We now consider strings of n 26 different characters from the alphabet.
For every n, p(n) is the number of strings of length n for which exactly one character comes lexicographically after its neighbour to the left.
What is the maximum value of p(n)?
```

Ok, to start solving the problem using doctest, I will use 3 files: `specif.txt`

will contain the tests I will use to specify the program, `countwords.py`

will contain the code of the program (the module being tested), and I will also use an executable `runspec.py`

to launch the tests. Each time I type `python runspec.py`

in a console, doctest will execute the tests contained in `specif.txt`

and display the failing tests if any. Here is the content of this launcher file

```
# runspec.py
from doctest import testfile
testfile("specif.txt")
```

Now, like in any TDD (test driven development) procedure, let's start by writing a few tests. I put the following in `specif.txt`

, which is a mix of comments/documentation and tests/specifications,

```
# specif.txt
This is the specification file for the countwords project
The module 'countwords' can be imported
>>> import countwords
Each letter will be identified by a number in [0, 26[. A valid word
has the form
w = w1 w2 x y w3 w4
where x < y are letters, w1 is an decreasing sequence of letters > y,
w2 and w3 are decreasing sequences of letters in ]x,y[ with no common
element, and w4 is a decreasing sequence of letters < x. if n, n1, n2,
n3, n4 are the lengths of w, w1, w2, w3, w4, then one has
n = 2 + n1 + n2 + n3 + n4
We wish to count the possible words for a given n. We'll do this by
enumeration. First we generate all possible choices for the pair x, y
>>> from countwords import findpairs
>>> pairs = findpairs()
Let's check the output
>>> isinstance(pairs, list)
True
>>> len(pairs) == 25*26/2
True
All pairs are different
>>> len(set(pairs)) == len(pairs)
True
All pairs satisfy the bounds
>>> filter(lambda (x, y): x < 0 or x >= y or y >= 26, pairs)
[]
```

Now I can start running `python runspec.py`

. Doctest will complain that there is no module `countwords`

, and, after I create this module, it will complain until the module contains a function `findpairs`

with the desired properties. Here is how to implement one

```
# countwords.py
def findpairs():
L = []
for x in range(26):
for y in range(x+1, 26):
L.append((x,y))
return L
```

Now doctest is happy, and I realize that I will need some function to compute the binomial coefficients, so I add the following tests to my specification file

```
#specif.txt
The function 'combi' will return the number of combinations of k
objects among n
>>> from countwords import combi
Let's check it works
>>> [combi(k, 5) for k in range(6)]
[1, 5, 10, 10, 5, 1]
We can add any test we like
>>> combi(4, 10) == (10*9*8*7)/(4*3*2*1)
True
```

Again, doctest will complain that such a function does not exist, and may be then that it doesn't work as expected, so I add it to my module

```
# countwords.py
def combi(k, n):
if k < 0 or k > n:
raise ValueError, "k must be smaller than n"
if n-k < k:
k = n-k
a, b = 1, 1
for p in range(1, k+1):
a *= (n+1-p)
b *= p
return a/b
```

When doctest is satisfied, I add a few more spectifications (tests)

```
# specif.txt
Now, for a given pair x, y and a given n >= 2, we want to generate all
possible values of (k1, k2, k3, k4). The requirements are that
0 <= k1, k2, k3, k4
k1 + k2 + k3 + k4 + 2 = n
k1 <= 25 - y
k1 + k2 <= y - x - 1
k4 <= x
We suppose that countwords contains a generator 'genks' for those k's
>>> from countwords import genks
>>> L = list(genks(4, 10, 4))
>>> print L
[(0, 0, 0, 2), (0, 0, 1, 1), (0, 0, 2, 0), (0, 1, 0, 1), (0, 1, 1, 0), (0, 2, 0, 0), (1, 0, 0, 1), (1, 0, 1, 0), (1, 1, 0, 0), (2, 0, 0, 0)]
We check the conditions
>>> for k1, k2, k3, k4 in genks(4, 10, 6):
... assert(min(k1, k2, k3, k4) >= 0)
... assert(k1+k2+k3+k4+2 == 6)
... assert(k1 <= 25 - 10)
... assert(k1 + k2 <= 5)
... assert(k4 <= 4)
The number of admissible words for given x, y, k1, ..., k4 must be
>>> from countwords import countadmissible
>>> x, y, k = 4, 10, (2, 1, 1, 3)
>>> countadmissible(x, y, k) == (
... combi(k[0], 25-y) * combi(k[1], y-x-1)
... * combi(k[2], y-x-1-k[1]) * combi(k[3], x))
True
```

And I add the corresponding features in `countwords.py`

until all tests pass

```
def genks(x, y, n):
n1 = n-2
z1 = y-x-1
for k1 in range(1+min(25-y, n1)):
n2 = n1-k1
for k2 in range(1+min(n2, z1)):
n3 = n2-k2
z2 = z1-k2
for k3 in range(1+min(n3, z2)):
if n3-k3 < x:
yield (k1, k2, k3, n3-k3)
def countadmissible(x, y, k):
k1, k2, k3, k4 = k
return ( combi(k1, 25-y) * combi(k2, y-x-1)
* combi(k3, y-x-1-k2) * combi(k4, x))
```

Here is the rest of the specifications and the code (the tests are rather incomplete here, I'm not even sure the results are correct, but you can add tests if you want)

```
# specif.txt
Now a function to count all words for a given pair x, y and a given n
>>> from countwords import countthem
>>> countthem(4, 10, 6) > 0
True
Now count all words for a given n
>>> from countwords import countall
>>> countall(6) > 0
True
Find the max value
>>> print max([countall(n) for n in range(2, 26)])
213618187786
```

The program

```
# countwords.py
def countthem(x, y, n):
cnt = 0
for k in genks(x, y, n):
cnt += countadmissible(x, y, k)
return cnt
def countall(n):
cnt = 0
for x, y in findpairs():
cnt += countthem(x, y, n)
return cnt
```

I hope I convinced you that doctest can really be used for TDD. Note also that existing doctests can be integrated in unittest suite, so there is no loss in using doctest if you want to use unittest later. Hope the post is useful ;)

I also created a small green-yellow-red light widget in wxpython to show if tests failed or not, but that's another story ;).

0

Hi

Thank you very much,

The article is very good.

Similarly Can anybody share material on nose Unit Testing Framework.

how to write a plugin to nosetests and reading through the libraries of nosetests with some examples.

This article has been dead for over six months. Start a new discussion instead.

Recommended Articles

the function that I created to find the ...

Hi. so this is actually a continuation from another question of mineHere but i was advised to start a new thread as the original question was already answered.

This is the result of previous question answered :

code for the listbox - datagridview interaction

At the top of the code ...

Hi. I have a form with list box : lst_product, datagridview : grd_order and button: btn_addline. lst_product has a list of product ids selected from database (MS Acess 2013) , grd_order is by default empty except for 2 headers and btn_addline adds rows to grd_order.

btn_addline :

`Private Sub btn_addline_Click(ByVal ...`