Python Testing Fundamentals
Upcoming SlideShare
Loading in...5
×
 

Python Testing Fundamentals

on

  • 1,499 views

Slides from PyOhio, a brief tour of unittest and doctest.

Slides from PyOhio, a brief tour of unittest and doctest.

Statistics

Views

Total Views
1,499
Views on SlideShare
1,497
Embed Views
2

Actions

Likes
2
Downloads
28
Comments
0

1 Embed 2

https://twitter.com 2

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Python Testing Fundamentals Python Testing Fundamentals Presentation Transcript

  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • mage Python Testing Fundamentals http://drunkenpython.org/pytestfund.pdf http://drunkenpython.org/pyohio.zip http://drunkenpython.org/pyohio.tgz ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • About This Tutorial •Fundamental •Python 3.2.3 •Assertion •Unittest •Doctest ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Why Test? “Untested Code is Broken Code” - Phillip von Weitershausen ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Why Test? •Tests help you design good code •Test help you find bugs •Tests help you document code ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements•Assert statements use the assert keyword•Assert statements raise AssertionError•Based on bool test expressions ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements keyword >>> assert 1 == 1 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements expression >>> assert 1 == 1 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements >>> assert expression is almost the same as: >>> if not expression: ... raise AssertionError() >>> ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements >>> assert 1 == 1 >>> assert 1 == 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError >>> ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements•A second optional expression on the assert statement provides a message for the AssertionError•This helps you distinguish one assertion from another ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements second expression >>> assert 1 == 2, "Reality check" ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements assert expression1, expression2 is almost the same as: if not expression1: raise AssertionError(expression2) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements >>> assert 1 == 2, "Reality check" Traceback (most recent call last): File "<stdin>", line 1, in <module> AssertionError: Reality check >>> ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements•Assertions may be sprinkled liberally throughout your code to check that your code is running as expected•Think of assertions as Pythons reality check•You decide what "reality" is ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements bigbiz.py: profit = bottom_line(today) assert profit > 0, "Unacceptable!" projection = growth(tomorrow) assert projection > profit, "UR fired!" ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements•Assertions usually generate unhandled exceptions which halt your program ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements bigbiz.py: def bottom_line(timestamp): """Compute the profit on date.""" Text return -1e6 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements > python bigbiz.py Traceback (most recent call last): File "bigbiz.py", line 20, in <module> assert profit > 0, "Unacceptable!" AssertionError: Unacceptable! > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements•By using Pythons -i command line switch, you may interactively inspect what went wrong where the assertion was raised ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 >>> ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements•By using Pythons -i command line switch, you may use also Pythons debugger to see what went wrong at the point of assertion ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements•Assertions may be turned off by "optimizing" Python with the -O command line switch ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements > python -O >>> assert 1 == 2 >>> ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements > python -O bigbiz.py Profit is -1000000.00 USD. Projected profit is -2000000.00 USD. > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Assert Statements•Its 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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] >>> ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestCase Class•unittest.TestCase is a class•You create TestCase subclasses•You add methods whose names start with "test" to your TestCase subclass ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestCase Class test_operator.py: """Demonstrate the unittest module.""" Text import operator import unittest ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestCase Class test_operator.py: class TestOperator(unittest.TestCase): Text """Test the operator module.""" ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestCase Class test_operator.py: def test_add(self): """Test the add function.""" Text self.assertEqual(operator.add(2, 2), 2 + 2) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestCase Class test_operator.py: def test_sub(self): """Test the sub function.""" Text self.assertEqual(operator.sub(4, 2), 4 - 2) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestCase Class test_operator.py: def test_mul(self): """Test the mul function.""" Text self.assertEqual(operator.mul(2, 2), 2 * 2) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestCase Class test_operator.py: if __name__ == "__main__": Text unittest.main() ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 scripts namespace are loaded and run ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • main Class > python test_operator.py ... -------------------------------------- Ran 3 tests in 0.001s OK > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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! ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestRunner Class TestCase unittest.main (superclass) (instance) createTests() TestOperator discover() TestLoader (subclass) (instance) runTests() TestOperator run() TestRunner (instance) (instance) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • main Class test_operator.py: if __name__ == "__main__": Text unittest.main(verbosity=2) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 instances __dict__ attribute•Dictionaries are unordered•unittest.main() runs the test methods in your Pythons built-in order for strings ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestCase Class test_operator.py: def test_add(self): """Test the add function.""" Text self.assertEqual(operator.add(2, 2), 2 + 3) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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) > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestCase Class test_operator.py: def test_add(self): """Test the add function.""" Text self.assertEqual(operator.add(2, 2), 2 + "2") ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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) > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestResult Class > python -m unittest test_operator ... -------------------------------------- Ran 3 tests in 0.000s OK > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestResult Class > python -m unittest test_operator.TestOperator ... -------------------------------------- Ran 3 tests in 0.000s OK > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestResult Class > python -m unittest test_operator.TestOperator.test_add . -------------------------------------- Ran 1 test in 0.000s OK > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestResult Class > python -m unittest test_operator.py ... -------------------------------------- Ran 3 tests in 0.000s OK > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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. ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • load_tests Protocol > python -m unittest ....... -------------------------------------- Ran 7 tests in 0.000s OK > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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. ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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! ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestCase Class test_operatorNG.py: def test_add_str(self): """Test bad args for add.""" Text with self.assertRaises(TypeError): operator.add(2, "2") ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestCase Class > python -m unittest test_operatorNG .... -------------------------------------- Ran 4 tests in 0.000s OK > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestCase Class•Plus many more! >>> [attr for attr ... in dir(unittest.TestCase) ... if attr.startswith(assert)] ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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. ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestSuite Class test_operatorNG2.py: """Demonstrate the unittest module.""" Text import operator import unittest ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestSuite Class test_operatorNG2.py: class TestAdd(unittest.TestCase): Text """Test the add function.""" ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestSuite Class test_operatorNG2.py: def test_add_int(self): """Test with ints.""" Text self.assertEqual(operator.add(2, 2), 2 + 2) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestSuite Class test_operatorNG2.py: def test_add_str(self): """Test with strs.""" Text with self.assertRaises(TypeError): operator.add(2, "2") ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestSuite Class test_operatorNG2.py: class TestSub(unittest.TestCase): Text """Test the sub function.""" ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestSuite Class test_operatorNG2.py: def test_sub_int(self): """Test with ints.""" Text self.assertEqual(operator.sub(4, 2), 4 - 2) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestSuite Class test_operatorNG2.py: def test_sub_str(self): """Test with strs.""" Text with self.assertRaises(TypeError): operator.sub(4, "2") ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestSuite Class test_operatorNG2.py: class TestMul(unittest.TestCase): Text """Test the mul function.""" ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestSuite Class test_operatorNG2.py: def test_mul_int(self): """Test with ints.""" Text self.assertEqual(operator.mul(2, 2), 2 * 2) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestSuite Class test_operatorNG2.py: def test_mul_str(self): """Test with strs.""" Text self.assertEqual(operator.mul(2, "2"), "22") ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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")) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestSuite Class test_operatorNG2.py: if __name__ == "__main__": Text unittest.main() ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestSuite Class•Objects you have bound in the test_operatorNG2 namespace: •TestAdd class •TestSub class •TestMul class •str_suite instance ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestSuite Class > python test_operatorNG2.py ...... -------------------------------------- Ran 6 tests in 0.001s OK > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • TestSuite Class > python -m unittest test_operatorNG2.str_suite ... -------------------------------------- Ran 3 tests in 0.000s OK > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests pyohio-+ bin/python pycamp-+ __init__.py setup.py fibonacci.py triangle.py tests-+ __init__.py test_fibonacci.py test_triangular.py ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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] >>> ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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] >>> ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests pycamp/tests/test_fibonacci.py: """Tests for the fibonacci module.""" Text from pycamp import fibonacci import unittest ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests pycamp/tests/test_fibonacci.py: class TestFibonacci(unittest.TestCase): Text """Test fibonaccis functions.""" ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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, ] ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests pycamp/tests/test_fibonacci.py: def test_fib_100(self): """Test fib for numbers < 100.""" Text self.assertEqual(fibonacci.fib(100), self.lessThan100) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests pycamp/tests/test_fibonacci.py: def test_fib_10(self): """Test fib for numbers < 10.""" Text self.assertEqual(fibonacci.fib(10), self.lessThan100[:5]) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests pycamp/tests/test_fibonacci.py: if __name__ == "__main__": Text unittest.main() ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests pycamp/tests/test_triangular.py: """Tests for the triangular module.""" Text from pycamp import triangular import unittest ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests pycamp/tests/test_triangular.py: class TestTriangular(unittest.TestCase): Text """Test triangulars functions.""" ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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, ] ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests pycamp/tests/test_triangular.py: def test_tri_100(self): """Test tri for numbers < 100.""" Text self.assertEqual(triangular.tri(100), self.lessThan100) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests pycamp/tests/test_triangular.py: def test_tri_10(self): """Test tri for numbers < 10.""" Text self.assertEqual(triangular.tri(10), self.lessThan100[:3]) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests pycamp/tests/test_triangular.py: if __name__ == "__main__": Text unittest.main() ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests > python -m unittest discover -s pycamp -t . .... -------------------------------------- Ran 4 tests in 0.001s OK > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests pycamp/setup.py: """Setup for pycamp package.""" Text from setuptools import setup, find_packages ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests pycamp/setup.py: setup( name="pycamp", version="1.0", Text packages=find_packages(), author="Chris Calloway", author_email="cbc@chriscalloway.org", ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests pycamp/setup.py: description="Testing Fundamentals", license="PSF", Text keywords="testing pycamp", url="http://pycamp.org", test_suite="pycamp.tests", ) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests > python pycamp/setup.py test running test ..lots of output omitted for brevity.. -------------------------------------- Ran 4 tests in 0.001s OK > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests > python pycamp/setup.py test ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests > python pycamp/setup.py install ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Organizing Tests > python pycamp/setup.py register ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Doctest Module•Informally, even without written tests, you probably already test your code by simply using it as it was meant to be used•Youve probably imported your code at a Python prompt and inspect how it works manually•You just dont yet have a way of repeating that informal testing in an automated manner•What you need is the doctest module ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Doctest Module pyohio-+ bin/python pycampNG-+ __init__.py setup.py fibonacci.py triangle.py tests-+ __init__.py test_pycamp.py ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Doctest Module pycampNG/fibonacci.py: """A module of functions about non-zero Fibonacci numbers. Text >>> import fibonacci >>> """ ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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. ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 """ ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Doctest Module pycampNG/triangular.py: """A module of functions about non-zero triangular numbers. Text >>> import triangular >>> """ ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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. ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 """ ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Doctest Module > python -m doctest pycampNG/fibonacci.py > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Doctest Module > python -m doctest -v pycampNG/fibonacci.py Trying: import fibonacci Expecting nothing ok ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Doctest Module Trying: lessThan100 = [1, 2, 3, 5, 8, 13, 21, 34, 55, 89] Expecting nothing ok ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Doctest Module Trying: fib(100) == lessThan100 Expecting: True ok ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Doctest Module Trying: fib(10) == lessThan100[:5] Expecting: True ok ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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. > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Doctest Module > python -m doctest pycampNG/triangular.py > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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]))])) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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]))])) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Doctest Module > python pycampNG/fibonacci.py test > python pycampNG/fibonacci.py 100 1 2 3 5 8 13 21 34 55 89 > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • load_tests Protocol def load_tests(loader, tests, pattern): ... return tests ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • load_tests Protocol unittest.TestLoader def load_tests(loader, tests, pattern): ... return tests ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • load_tests Protocol def load_tests(loader, unittest.TestSuite tests, pattern): ... return tests ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • load_tests Protocol def load_tests(loader, tests, "test*.py" pattern): ... return tests ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • load_tests Protocol def load_tests(loader, tests, pattern): ... return tests ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • load_tests Protocol pycampNG/tests/test_pycamp.py: import doctest Text from pycampNG import fibonacci from pycampNG import triangular ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • load_tests Protocol pycamp/setup.py: setup( name="pycampNG", Text ... test_suite="pycampNG.tests", ) ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • load_tests Protocol > python pycampNG/setup.py test running test ..lots of output omitted for brevity.. -------------------------------------- Ran 4 tests in 0.001s OK > ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Python Testing Tools Taxonomy ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • 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 ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Test Driven Development ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Tomorrow 12:15pm in Barbie Tootle ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People
  • Thank You pycamp@trizpug.org ProgrammingPyCamp™ Copyright © 2012 Trizpug For The People