Taming the Snake:
Python unit tests
Esteban Manchado Velázquez
Assumptions
- Basic Python knowledge
- Interested in unit tests
Contents
- The unittest module ("PyUnit")
- Simple conventions
- Some advice
- The nosetests utility
The unittest module
- Python implementation of jUnit and friends
- Object-Oriented
- Supports preparation (setUp/tearDown)
- Supports test suites, test runners, etc.
- Based on assertions
Example unit test
import unittest
class WidgetSizeTestCase(unittest.TestCase):
def setUp(self):
self.widget = Widget('The widget')
def tearDown(self):
self.widget.dispose()
self.widget = None
def testDefaultSize(self):
self.failUnless(self.widget.size() == (50,50),
'incorrect default size')
def testResize(self):
self.widget.resize(100,150)
self.failUnless(self.widget.size() == (100,150),
'wrong size after resize')
Assertions
- failUnless (aka. assertTrue)
- failUnlessEqual (aka. assertEqual)
- failIfEqual (aka. assertNotEqual)
- failUnlessAlmostEqual (aka. assertAlmostEqual)
- failIfAlmostEqual (aka. assertNotAlmostEqual)
- failUnlessRaises (aka. assertRaises)
- failIf (aka. assertFalse)
- Most of these methods accept a description. Use it!
How do I run this?
- Why, with a testrunner of course!
- Easiest: nosetests, more about it later
- Basic: call unittest.main()
- A bit better: load modules with unittest.loadTestsFromModule()
import foo, unittest
suite = unittest.TestLoader(). \
loadTestsFromModule(foo)
unittest.TextTestRunner().run(suite)
Conventions
- Create a tests/ directory
- Name your test files by the area/module:
test_serializer.py
- Create classes for each test case:
BinarySerializationTestCase
- Create methods for each concrete test:
testNullSerialization
By example
dev@machine$ ls -l tests/
total 4
-rw-r--r-- 1 dev dev 486 2009-08-31 12:48 test_serializer.py
dev@machine$ cat tests/test_serializer.py
import unittest, serializer
class BinarySerializationTestCase(unittest.TestCase):
def setUp(self):
self.serializer = Serializer()
def tearDown(self):
self.serializer = None
def testNullSerialization(self):
string = "\x00foobar\x00"
result = self.serializer.serialize(string)
self.failUnlessEqual(string,
self.serialize.unserialize(result),
'Serializing nulls should work')
Free advice
- Don't try only easy/basic cases
- Write a test for your bugs
- Write the test first, then fix the code
- Run the tests before committing
- Don't focus on the current implementation
- Test whatever should work, not "symptoms"
- Test in a controlled environment (reliable!)
Nosetests
- Collects and runs tests
- Simple conventions
- No need to "declare" your tests
- Lots of features I won't talk about
- Basically, run nosetests and be happy
- http://code.google.com/p/python-nose/
By example
# All tests
nosetests
# All tests, show test names
nosetests -v
# Only this file
nosetests test_serializer.py
# Only this testcase
nosetests test_serializer.py:BinarySerializationTestCase
# Only this method inside this testcase!!!11
nosetests test_serializer.py:BinarySerializationTestCase. \
testNullSerialization
Wrapping up
- Module unittest
- Files with testcases (classes)
- test* methods
- setUp/tearDown for preparations
- failUnless et al. for actual checks
- nosetests to execute your tests
TESTS, MÖTHAFÖCKA, DO YOU WRITE THEM‽