Mais conteúdo relacionado
Semelhante a Python Testing Fundamentals (20)
Python Testing Fundamentals
- 1. mage
Python Testing Fundamentals
Python Testing Fundamentals
Saturday, July 28, 2012
PyOhio
Ohio State University
Columbus, OH
Chris Calloway
University of North Carolina
Department of Marine Sciences
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 2. mage
Python Testing Fundamentals
http://drunkenpython.org/pytestfund.pdf
http://drunkenpython.org/pyohio.zip
http://drunkenpython.org/pyohio.tgz
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 3. About This Tutorial
•Fundamental
•Python 3.2.3
•Assertion
•Unittest
•Doctest
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 4. Why Test?
“Untested Code is Broken Code”
- Phillip von Weitershausen
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 5. Why Test?
•Tests help you design good code
•Test help you find bugs
•Tests help you document code
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 6. Assert Statements
•Assert statements use the assert keyword
•Assert statements raise AssertionError
•Based on bool test expressions
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 7. Assert Statements
keyword
>>> assert 1 == 1
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 9. Assert Statements
>>> assert expression
is almost the same as:
>>> if not expression:
... raise AssertionError()
>>>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 10. Assert Statements
>>> assert 1 == 1
>>> assert 1 == 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError
>>>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 11. Assert Statements
•A second optional expression on the assert
statement provides a message for the
AssertionError
•This helps you distinguish one assertion from
another
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 12. Assert Statements
second expression
>>> assert 1 == 2, "Reality check"
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 13. Assert Statements
assert expression1, expression2
is almost the same as:
if not expression1:
raise AssertionError(expression2)
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 14. Assert Statements
>>> assert 1 == 2, "Reality check"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AssertionError: Reality check
>>>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 15. Assert Statements
•Assertions may be sprinkled liberally throughout
your code to check that your code is running as
expected
•Think of assertions as Python's reality check
•You decide what "reality" is
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 16. Assert Statements
bigbiz.py:
profit = bottom_line(today)
assert profit > 0, "Unacceptable!"
projection = growth(tomorrow)
assert projection > profit, "UR fired!"
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 18. Assert Statements
bigbiz.py:
def bottom_line(timestamp):
"""Compute the profit on date."""
Text
return -1e6
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 19. Assert Statements
> python bigbiz.py
Traceback (most recent call last):
File "bigbiz.py", line 20, in <module>
assert profit > 0, "Unacceptable!"
AssertionError: Unacceptable!
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 20. Assert Statements
•By using Python's -i command line switch, you
may interactively inspect what went wrong where
the assertion was raised
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 21. Assert Statements
> python -i bigbiz.py
Traceback (most recent call last):
File "bigbiz.py", line 20, in <module>
assert profit > 0, "Unacceptable!"
AssertionError: Unacceptable!
>>> profit
-1000000.0
>>>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 22. Assert Statements
•By using Python's -i command line switch, you
may use also Python's debugger to see what
went wrong at the point of assertion
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 23. Assert Statements
> python -i bigbiz.py
Traceback (most recent call last):
File "bigbiz.py", line 20, in <module>
assert profit > 0, "Unacceptable!"
AssertionError: Unacceptable!
>>> import pdb
>>> pdb.pm()
> /Users/cbc/pyohio/bigbiz.py(20)<module>()
-> assert profit > 0, "Unacceptable!"
(Pdb) profit
-1000000.0
(Pdb)
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 24. Assert Statements
•Assertions may be turned off by "optimizing"
Python with the -O command line switch
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 25. Assert Statements
> python -O
>>> assert 1 == 2
>>>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 26. Assert Statements
> python -O bigbiz.py
Profit is -1000000.00 USD.
Projected profit is -2000000.00 USD.
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 27. Assert Statements
•It's a fine line whether assert statements are
testing or debugging
•Assert statements are slightly more sophisticated
than using print
•But assert statements form the basis for testing in
Python
•In Python, a test is an assertion of an expected
result
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 28. Unittest Module
•Unittest is "batteries included" in Python
•Unittest helps you separate test code from the
code under test
•Unittest helps you write tests before code
•Unittest helps you organize and discover all your
tests
•Unittest hooks into many other Python tools
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 29. Unittest Module
>>> import unittest
>>> dir(unittest)
['BaseTestSuite', 'FunctionTestCase', 'SkipTest',
'TestCase', 'TestLoader', 'TestProgram', 'TestResult',
'TestSuite', 'TextTestResult', 'TextTestRunner',
'_TextTestResult', '__all__', '__builtins__',
'__cached__', '__doc__', '__file__', '__name__',
'__package__', '__path__', '__unittest', 'case',
'defaultTestLoader', 'expectedFailure', 'findTestCases',
'getTestCaseNames', 'installHandler', 'loader', 'main',
'makeSuite', 'registerResult', 'removeHandler',
'removeResult', 'result', 'runner', 'signals', 'skip',
'skipIf', 'skipUnless', 'suite', 'util']
>>>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 30. TestCase Class
•unittest.TestCase is a class
•You create TestCase subclasses
•You add methods whose names start with "test"
to your TestCase subclass
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 31. TestCase Class
•Each test method you supply in your subclass
executes a TestCase supplied method whose
name starts with "assert"
•A TestCase provides what is known as a "test
fixture" in testing parlance
•The unittest module provides many ways to
run the test methods of your text fixture
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 32. TestCase Class
test_operator.py:
"""Demonstrate the unittest module."""
Text
import operator
import unittest
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 33. TestCase Class
test_operator.py:
class TestOperator(unittest.TestCase):
Text
"""Test the operator module."""
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 34. TestCase Class
test_operator.py:
def test_add(self):
"""Test the add function."""
Text
self.assertEqual(operator.add(2, 2),
2 + 2)
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 35. TestCase Class
test_operator.py:
def test_sub(self):
"""Test the sub function."""
Text
self.assertEqual(operator.sub(4, 2),
4 - 2)
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 36. TestCase Class
test_operator.py:
def test_mul(self):
"""Test the mul function."""
Text
self.assertEqual(operator.mul(2, 2),
2 * 2)
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 38. main Class
•unittest.main is also a class
•unittest.main is normally only used within a
script containing test fixtures
•When unittest.main is instantiated, all of the
tests in the script's namespace are loaded and
run
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 39. main Class
> python test_operator.py
...
--------------------------------------
Ran 3 tests in 0.001s
OK
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 40. TestLoader Class
•Notice that nowhere does it appear that your
script has instantiated your TestCase subclass or
execute any of its methods
•unittest.main instead instantiates a special
unittest.TestLoader class which has
methods to search your module for TestCase
classes and instantiate them
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 41. TestRunner Class
•Once the TestLoader instance created by
unittest.main discovers and instantiates the
TestCase in your script, unittest.main
instantiates a special unittest.TestRunner
class which has methods to run the methods of
your TestCase instances
•unittest.main takes care of handling
TestLoaders and TestRunners for you!
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 42. TestRunner Class
TestCase unittest.main
(superclass) (instance)
createTests()
TestOperator discover() TestLoader
(subclass) (instance)
runTests()
TestOperator run() TestRunner
(instance) (instance)
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 43. main Class
test_operator.py:
if __name__ == "__main__":
Text
unittest.main(verbosity=2)
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 44. main Class
> python test_operator.py
test_add (__main__.TestOperator)
Test the add function. ... ok
test_mul (__main__.TestOperator)
Test the mul function. ... ok
test_sub (__main__.TestOperator)
Test the sub function. ... ok
--------------------------------------
Ran 3 tests in 0.001s
OK
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 45. main Class
•Notice that your tests did not run in the same
order in which they were defined
•unittest.main loads the test methods from
your TestCase instance's __dict__ attribute
•Dictionaries are unordered
•unittest.main() runs the test methods in your
Python's built-in order for strings
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 46. TestCase Class
test_operator.py:
def test_add(self):
"""Test the add function."""
Text
self.assertEqual(operator.add(2, 2),
2 + 3)
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 47. TestCase Class
> python test_operator.py
F..
======================================
FAIL: test_add (__main__.TestOperator)
Test the add function.
--------------------------------------
Traceback (most recent call last):
File "test_operator.py", line 13, in test_add
self.assertEqual(operator.add(2, 2), 2 + 3)
AssertionError: 4 != 5
--------------------------------------
Ran 3 tests in 0.082s
FAILED (failures=1)
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 48. TestCase Class
test_operator.py:
def test_add(self):
"""Test the add function."""
Text
self.assertEqual(operator.add(2, 2),
2 + "2")
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 49. TestCase Class
> python test_operator.py
E..
================================================
ERROR: test_add (__main__.TestOperator)
Test the add function.
------------------------------------------------
Traceback (most recent call last):
File "test_operator.py", line 13, in test_add
self.assertEqual(operator.add(2, 2), 2 + "2")
TypeError: unsupported operand type(s) for +:
int' and 'str'
------------------------------------------------
Ran 3 tests in 0.001s
FAILED (errors=1)
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 50. TestResult Class
•Running a test results in one of three outcomes:
★ Success (expected result)
★ Failure (unexpected result)
★ Error (error running the test)
•The outcomes of all tests are accumulated in a
unittest.TestResult instance
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 51. TestResult Class
•Most of the time you will not need to create your
own TestResult instances
•Most of the ways you will run tests will
instantiate and report a TestResult for you
•But to run a test always requires a TestResult
instance somewhere
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 52. TestResult Class
> python -m unittest test_operator
...
--------------------------------------
Ran 3 tests in 0.000s
OK
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 53. TestResult Class
> python -m unittest
test_operator.TestOperator
...
--------------------------------------
Ran 3 tests in 0.000s
OK
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 54. TestResult Class
> python -m unittest
test_operator.TestOperator.test_add
.
--------------------------------------
Ran 1 test in 0.000s
OK
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 55. TestResult Class
> python -m unittest test_operator.py
...
--------------------------------------
Ran 3 tests in 0.000s
OK
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 56. TestResult Class
> python -m unittest -v test_operator
test_add (test_operator.TestOperator)
Test the add function. ... ok
test_mul (test_operator.TestOperator)
Test the mul function. ... ok
test_sub (test_operator.TestOperator)
Test the sub function. ... ok
--------------------------------------
Ran 3 tests in 0.001s
OK
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 57. TestResult Class
> python -m unittest -h
Usage: python -m unittest [options] [tests]
Options:
-h, --help Show this message
-v, --verbose Verbose output
-q, --quiet Minimal output
-f, --failfast Stop on first failure
-c, --catch Catch control-C and
display results
-b, --buffer Buffer stdout and stderr
during test runs
[tests] can be a list of any number of test
modules, classes and test methods.
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 58. load_tests Protocol
> python -m unittest
.......
--------------------------------------
Ran 7 tests in 0.000s
OK
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 59. load_tests Protocol
Alternative Usage: python -m unittest
discover [options]
Options:
-s directory Directory to start
discovery ('.' default)
-p pattern Pattern to match test
files
('test*.py' default)
-t directory Top level directory of
project (default to
start directory)
For test discovery all test modules must be
importable from the top level directory.
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 60. TestCase Class
•Notice that TestCase.assertEqual does not
appear to raise an unhandled AssertionError
•The TestRunner instance handles the
AssertionError for failing tests and updates the
TestResult instance
•TestCase.assertEqual is but one of many test
methods you may use in your tests
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 61. TestCase Class
Method Tests If
assertEqual(a, b) a == b
assertNotEqual(a, b) a != b
assertTrue(x) bool(x) is True
assertFalse(x) bool(x) is False
assertIs(a, b) a is b
assertIsNot(a, b) a is not b
assertIsNone(x) x is None
assertIsNotNone(x) x is not None
assertIn(a, b) a in b
assertNotIn(a, b) a not in b
assertIsInstance(a, b) isinstance(a, b)
assertNotIsInstance(a, b) not isinstance(a, b)
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 62. TestCase Class
•You not only need to test if your code produces
expected results, you also need to test if your
code handles unexpected results in an expected
manner!
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 63. TestCase Class
test_operatorNG.py:
def test_add_str(self):
"""Test bad args for add."""
Text
with self.assertRaises(TypeError):
operator.add(2, "2")
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 64. TestCase Class
> python -m unittest test_operatorNG
....
--------------------------------------
Ran 4 tests in 0.000s
OK
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 65. TestCase Class
Method Tests If
assertRaises(exception) exception raised
exception raised and
assertRaisesRegex(exception, regex)
message matches regex
assertWarns(warning) warning raised
warning raised and
assertWarnsRegex(warning, regex)
message matches regex
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 66. TestCase Class
Method Tests If
assertAlmostEqual(a, b) round(a-b, 7) == 0
assertNotAlmostEqual(a, b) round(a-b, 7) != 0
assertGreater(a, b) a > b
assertGreaterEqual(a, b) a >= b
assertLess(a, b) a < b
assertLessEqual(a, b) a <= b
assertRegex(s, re) s matches regex
assertNotRegex(s, re) s does not match regex
a and b have the same
assertCountEqual(a, b) elements in the same
number, regardless of order
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 67. TestCase Class
Method Compares
assertMultiLineEqual(a, b) Strings
assertSequenceEqual(a, b) Sequences
assertListEqual(a, b) Lists
assertTupleEqual(a, b) Tuples
assertSetEqual(a, b) Sets and Frozensets
assertDictEqual(a, b) Dictionaries
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 68. TestCase Class
•Plus many more!
>>> [attr for attr
... in dir(unittest.TestCase)
... if attr.startswith('assert')]
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 69. TestCase Class
>>> help(unittest.TestCase.
... assertDictContainsSubset)
Help on function assertDictContainsSubset in
module unittest.case:
assertDictContainsSubset(self, subset,
dictionary, msg=None)
Checks whether dictionary is a superset of
subset.
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 72. TestSuite Class
test_operatorNG2.py:
def test_add_int(self):
"""Test with ints."""
Text
self.assertEqual(operator.add(2, 2),
2 + 2)
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 73. TestSuite Class
test_operatorNG2.py:
def test_add_str(self):
"""Test with strs."""
Text
with self.assertRaises(TypeError):
operator.add(2, "2")
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 75. TestSuite Class
test_operatorNG2.py:
def test_sub_int(self):
"""Test with ints."""
Text
self.assertEqual(operator.sub(4, 2),
4 - 2)
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 76. TestSuite Class
test_operatorNG2.py:
def test_sub_str(self):
"""Test with strs."""
Text
with self.assertRaises(TypeError):
operator.sub(4, "2")
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 78. TestSuite Class
test_operatorNG2.py:
def test_mul_int(self):
"""Test with ints."""
Text
self.assertEqual(operator.mul(2, 2),
2 * 2)
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 79. TestSuite Class
test_operatorNG2.py:
def test_mul_str(self):
"""Test with strs."""
Text
self.assertEqual(operator.mul(2, "2"),
"22")
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 80. TestSuite Class
test_operatorNG2.py:
str_suite = unittest.TestSuite()
Text
str_suite.addTest(TestAdd("test_add_str"))
str_suite.addTest(TestSub("test_sub_str"))
str_suite.addTest(TestMul("test_mul_str"))
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 82. TestSuite Class
•Objects you have bound in the
test_operatorNG2 namespace:
•TestAdd class
•TestSub class
•TestMul class
•str_suite instance
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 83. TestSuite Class
> python test_operatorNG2.py
......
--------------------------------------
Ran 6 tests in 0.001s
OK
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 84. TestSuite Class
> python -m unittest
test_operatorNG2.str_suite
...
--------------------------------------
Ran 3 tests in 0.000s
OK
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 85. TestSuite Class
> python -m unittest -v
test_operatorNG2.str_suite
test_add_str (test_operatorNG2.TestAdd)
Test with strs. ... ok
test_sub_str (test_operatorNG2.TestSub)
Test with strs. ... ok
test_mul_str (test_operatorNG2.TestMul)
Test with strs. ... ok
--------------------------------------
Ran 3 tests in 0.001s
OK
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 86. Organizing Tests
pyohio-+
bin/python
pycamp-+
__init__.py
setup.py
fibonacci.py
triangle.py
tests-+
__init__.py
test_fibonacci.py
test_triangular.py
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 87. Organizing Tests
> python pycamp/fibonacci.py 100
1 2 3 5 8 13 21 34 55 89
> python
>>> from pycamp.fibonacci import fib
>>> fib(100)
[1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
>>>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 88. Organizing Tests
> python pycamp/triangular.py 100
1 3 6 10 15 21 28 36 45 55 66 78 91
> python
>>> from pycamp.triangular import tri
>>> tri(100)
[1, 3, 6, 10, 15, 21, 28, 36, 45, 55,
66, 78, 91]
>>>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 91. TestCase Class
•TestCase also supplies methods you override
•TestCase.setUp() is called before every test
method you supply in your subclass
•TestCase.tearDown() is called after every
test method you supply in your subclass
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 92. TestCase Class
•Your TestCase subclasses, along with all the
test methods you supply, and all the TestCase
supplied methods you override, possibly all
bundled up into a TestSuite, are collectively
known as a "test fixture" in testing parlance
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 93. Organizing Tests
pycamp/tests/test_fibonacci.py:
def setUp(self):
"""Test fixture build."""
Text
self.lessThan100 = [1, 2, 3, 5, 8,
13, 21, 34, 55,
89, ]
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 99. Organizing Tests
pycamp/tests/test_triangular.py:
def setUp(self):
"""Test fixture build."""
Text
self.lessThan100 = [1, 3, 6, 10, 15,
21, 28, 36, 45,
55, 66, 78, 91, ]
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 103. Organizing Tests
> python -m unittest discover
-s pycamp -t .
....
--------------------------------------
Ran 4 tests in 0.001s
OK
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 104. Organizing Tests
pycamp/setup.py:
"""Setup for pycamp package."""
Text
from setuptools import setup,
find_packages
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 105. Organizing Tests
pycamp/setup.py:
setup(
name="pycamp",
version="1.0", Text
packages=find_packages(),
author="Chris Calloway",
author_email="cbc@chriscalloway.org",
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 106. Organizing Tests
pycamp/setup.py:
description="Testing Fundamentals",
license="PSF",
Text
keywords="testing pycamp",
url="http://pycamp.org",
test_suite="pycamp.tests",
)
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 107. Organizing Tests
> python pycamp/setup.py test
running test
..lots of output omitted for brevity..
--------------------------------------
Ran 4 tests in 0.001s
OK
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 108. Organizing Tests
> python pycamp/setup.py test
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 109. Organizing Tests
> python pycamp/setup.py install
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 110. Organizing Tests
> python pycamp/setup.py register
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 111. Unittest Module
•unittest provides nice separation of tests from
code
•One person can write tests, while another writes
code to make tests pass
•unittest provides fine grain control over what
tests to run when
•unittest conforms to industry standard testing
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 112. Unittest Module
•However, it can be difficult to keep tests and
code in sync
•Also, writing code to test code can also be more
difficult than the code under test
•What about testing and debugging the test
code?
•Reading unittest tests is a poor way to figure
out how code works
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 113. Doctest Module
•Informally, even without written tests, you
probably already test your code by simply using
it as it was meant to be used
•You've probably imported your code at a Python
prompt and inspect how it works manually
•You just don't yet have a way of repeating that
informal testing in an automated manner
•What you need is the doctest module
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 114. Doctest Module
pyohio-+
bin/python
pycampNG-+
__init__.py
setup.py
fibonacci.py
triangle.py
tests-+
__init__.py
test_pycamp.py
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 115. Doctest Module
pycampNG/fibonacci.py:
"""A module of functions about non-zero
Fibonacci numbers.
Text
>>> import fibonacci
>>>
"""
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 116. Doctest Module
pycampNG/fibonacci.py:
def fib(n):
"""Return the sequence of non-zero
Fibonacci numbers less than n.
Text
fib(n) -> [0 < fibonacci numbers < n]
where n in an int.
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 117. Doctest Module
pycampNG/fibonacci.py:
>>> lessThan100 = [1, 2, 3, 5, 8, 13,
... 21, 34, 55, 89]
>>> fib(100) == lessThan100
Text
True
>>> fib(10) == lessThan100[:5]
True
"""
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 118. Doctest Module
pycampNG/triangular.py:
"""A module of functions about non-zero
triangular numbers.
Text
>>> import triangular
>>>
"""
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 119. Doctest Module
pycampNG/triangular.py:
def tri(n):
"""Return the sequence of non-zero
triangular numbers less than n.
Text
tri(n) -> [0 < triangular numbers < n]
where n in an int.
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 120. Doctest Module
pycampNG/triangular.py:
>>> lessThan100 = [1, 3, 6, 10, 15,
21, 28, 36, 45,
55, 66, 78, 91]
Text
>>> tri(100) == lessThan100
True
>>> tri(10) == lessThan100[:3]
True
"""
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 121. Doctest Module
> python -m doctest pycampNG/fibonacci.py
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 122. Doctest Module
> python -m doctest -v
pycampNG/fibonacci.py
Trying:
import fibonacci
Expecting nothing
ok
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 123. Doctest Module
Trying:
lessThan100 = [1, 2, 3, 5, 8, 13,
21, 34, 55, 89]
Expecting nothing
ok
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 124. Doctest Module
Trying:
fib(100) == lessThan100
Expecting:
True
ok
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 125. Doctest Module
Trying:
fib(10) == lessThan100[:5]
Expecting:
True
ok
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 126. Doctest Module
2 items passed all tests:
1 tests in fibonacci
3 tests in fibonacci.fib
4 tests in 2 items.
4 passed and 0 failed.
Test passed.
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 127. Doctest Module
> python -m doctest pycampNG/triangular.py
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 128. Doctest Module
pycampNG/fibonacci.py:
if __name__ == '__main__':
if sys.argv[1].lower() == 'test':
import doctest
Text
doctest.testmod()
else:
print(" ".join([str(x) for x in
fib(int(sys.argv[1]))]))
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 129. Doctest Module
pycampNG/triangular.py:
if __name__ == '__main__':
if sys.argv[1].lower() == 'test':
import doctest
Text
doctest.testmod()
else:
print(" ".join([str(x) for x in
tri(int(sys.argv[1]))]))
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 130. Doctest Module
> python pycampNG/fibonacci.py test
> python pycampNG/fibonacci.py 100
1 2 3 5 8 13 21 34 55 89
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 131. Doctest Module
> python pycampNG/triangular.py test
> python pycampNG/triangular.py 100
1 3 6 10 15 21 28 36 45 55 66 78 91
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 132. Doctest Module
•But what about setuptools.setup?
•The test_suite argument of setup triggers
unittest discovery, not doctest discovery
•What you need is a way to turn doctests into
unittests
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 133. Doctest Module
•The doctest.DocTestSuite() function
searches a module for doctests and converts
them into a unittest.TestSuite instance
•Now all you need is a way to communicate your
TestSuite instance(s) to unittest discovery
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 134. load_tests Protocol
def load_tests(loader,
tests,
pattern):
...
return tests
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 135. load_tests Protocol
unittest.TestLoader
def load_tests(loader,
tests,
pattern):
...
return tests
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 136. load_tests Protocol
def load_tests(loader,
unittest.TestSuite
tests,
pattern):
...
return tests
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 137. load_tests Protocol
def load_tests(loader,
tests,
"test*.py"
pattern):
...
return tests
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 138. load_tests Protocol
def load_tests(loader,
tests,
pattern):
...
return tests
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 140. load_tests Protocol
pycampNG/tests/test_pycamp.py:
def load_tests(loader, tests, pattern):
tests.addTests(
doctest.DocTestSuite(fibonacci))
Text
tests.addTests(
doctest.DocTestSuite(triangular))
return tests
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 141. load_tests Protocol
pycampNG/tests/test_pycamp.py:
def load_tests(loader, tests, pattern):
tests.addTests(
doctest.DocTestSuite(fibonacci))
Text
tests.addTests(
doctest.DocTestSuite(triangular))
return tests
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 142. load_tests Protocol
pycampNG/tests/test_pycamp.py:
def load_tests(loader, tests, pattern):
tests.addTests(
doctest.DocTestSuite(fibonacci))
Text
tests.addTests(
doctest.DocTestSuite(triangular))
return tests
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 143. load_tests Protocol
pycampNG/tests/test_pycamp.py:
def load_tests(loader, tests, pattern):
tests.addTests(
doctest.DocTestSuite(fibonacci))
Text
tests.addTests(
doctest.DocTestSuite(triangular))
return tests
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 145. load_tests Protocol
> python pycampNG/setup.py test
running test
..lots of output omitted for brevity..
--------------------------------------
Ran 4 tests in 0.001s
OK
>
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 146. Doctest Module
Together, the doctest.DocTestSuite()
function and the load_tests protocol from the
unittest module enable you to use all the tools
available for unittests with doctests
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 148. Test Driven Development
•Doctests enable you to do Test Driven
Development (TDD)
•TDD is where you write tests for your code
before you write code
•Then you write code to make your tests pass
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 149. Test Driven Development
•When all your tests pass, then you have finished
coding
•You can develop your code incrementally,
writing one test at a time, then getting that one
test to pass
•That means you can stop coding at any time
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 150. Test Driven Development
•If your code needs more features, then what you
really need is more tests, and code which makes
those tests pass
•Writing tests lets you see what the API for your
code is up front, instead of having it designed
haphazardly
•Writing tests can provide the documentation for
your code
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 152. Tomorrow 12:15pm in Barbie Tootle
Programming
PyCamp™ Copyright © 2012
Trizpug For The People
- 153. Thank You
pycamp@trizpug.org
Programming
PyCamp™ Copyright © 2012
Trizpug For The People