Python. Unit Test Generation

Only the lazy have not yet written about the need to write tests. But let's be honest, writing tests is often boring. Especially for legacy code. Hundreds of repeating, uniform lines. Boredom. What can be done with this?

image
A picture to attract attention. Beautiful python, huh? (Photo by Paweł Stefaniak)

Pythoscope


Fortunately, we are programmers - people are lazy, ready to kill a couple of weeks in order to solve the one-hour task in five minutes. So in a few minutes of googling in Yandex, I found colleagues in misfortune and an interesting solution to facilitate writing tests.

Install Pythoscope:

sudo pip install Pythoscope


Note
Unfortunately, the stable branch has unicode problems. For test purposes, it will work, but for real use it is better to use the dev-branch, since it works stably:

sudo apt-get install bzr
bzr branch lp:pythoscope
cd pythoscope/
python setup.py install




Test cat


image

To test a cat, we first need a cat. Let's write it:
# cat.py
class Cat(object):
    def __init__(self, name='Tom'):
        self.name = name

    def eat(self, food):
        if food == 'fish':
            return 'Yummy!'
        else:
            return 'Ugh!'


Now go to the folder with cat.py and initialize the Pythoscope:

pythoscope --init


The team will create a .pythoscope folder where all the information related to the Pythoscope is stored. And now, finally, the generation of the tests themselves:

pythoscope cat.py


We now have a tests folder with nested cat_test.py. In which ... Almost nothing:

# tests/cat_test.py
import unittest

class TestCat(unittest.TestCase):
    def test___init__(self):
        # cat = Cat(name)
        assert False # TODO: implement your test here

    def test_eat(self):
        # cat = Cat(name)
        # self.assertEqual(expected, cat.eat(food))
        assert False # TODO: implement your test here

if __name__ == '__main__':
    unittest.main()


Not much? Well, at least now there is a framework that will save us time when writing tests.
As it turned out, there is no magic - tests themselves will not be written. But not everything is lost, we can help Pythoscope understand what to run for tests. For this, the so-called “points of entry” are realizable — some use-case uses of our code.

Let's write points of entry for our kote:

# .pythoscope/points-of-entry/eat_fish_poe.py

from cat import Cat
Cat().eat('fish')


Run the test generation again:
pythoscope cat.py


Now it’s better, a method has been added to the test class that really tests something:
# tests/cat_test.py
...

def test_eat_returns_Yummy_for_fish_after_creation_with_Tom(self):
        cat = Cat('Tom')
        self.assertEqual('Yummy!', cat.eat('fish'))

...


But what if you don’t put kote to the fish? Error? It is necessary to check this:
# .pythoscope/points-of-entry/eat_tomato_poe.py

from cat import Cat
Cat().eat('tomato')


We generate:

pythoscope cat.py


Well, all that remains is to check:

nosetests

..
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK



Excellent! Successful and easy testing!