PyCon UK 2011 - Testing Workshop - Part 2

940 views

Published on

Overview of testing options in Python.

Published in: Technology, Business
1 Comment
1 Like
Statistics
Notes
No Downloads
Views
Total views
940
On SlideShare
0
From Embeds
0
Number of Embeds
4
Actions
Shares
0
Downloads
17
Comments
1
Likes
1
Embeds 0
No embeds

No notes for slide

PyCon UK 2011 - Testing Workshop - Part 2

  1. 1. Testing with Python
  2. 2. The REPL (aka the Python Interpreter)
  3. 3. Impress your friends: <ul><li>Read
  4. 4. Eval
  5. 5. Print
  6. 6. Loop </li></ul>
  7. 8. Pros <ul><li>Familiar to Pythonistas
  8. 9. Great for exploration
  9. 10. Simple to use </li></ul>
  10. 11. Cons <ul><li>Manual
  11. 12. Error-prone
  12. 13. Doesn't scale </li></ul>
  13. 14. We need automation!
  14. 15. And lo, there were Doctests!
  15. 16. def double(value): &quot;&quot;&quot; >>> double(1) 2 >>> double(2) 4 >>> double(&quot;foo&quot;) 'foofoo' >>> double(None) Traceback (most recent call last): File &quot;<stdin>&quot;, line 1, in <module> File &quot;<stdin>&quot;, line 2, in double TypeError: unsupported operand type(s) for *: 'NoneType' and 'int' &quot;&quot;&quot; return value * 2
  16. 17. def double(value): &quot;&quot;&quot; >>> double(1) 2 >>> double(2) 4 >>> double(&quot;foo&quot;) 'foofoo' >>> double(None) Traceback (most recent call last): File &quot;<stdin>&quot;, line 1, in <module> File &quot;<stdin>&quot;, line 2, in double TypeError: unsupported operand type(s) for *: 'NoneType' and 'int' &quot;&quot;&quot; return value * 2 A REPL session &quot;embedded&quot; in a function's doc string
  17. 19. Pros <ul><li>Automated
  18. 20. All the benefits of the REPL
  19. 21. Code and tests together </li></ul>
  20. 22. Cons <ul><li>Code and tests together
  21. 23. Can get big and clunky
  22. 24. Not very flexible </li></ul>
  23. 25. We need something better!
  24. 26. Behold: Unittest!
  25. 27. Unittest is part of the xUnit family xUnit started with SUnit for Smalltalk and has become a de facto standard The API is broadly similar across many different languages.
  26. 28. Pros <ul><li>Automated
  27. 29. Flexible
  28. 30. Scalable
  29. 31. Part of the standard library
  30. 32. Part of the xUnit family </li></ul>
  31. 33. Cons <ul><li>&quot;Unpythonic&quot; Java-esque API
  32. 34. Neglected until recently
  33. 35. Code and tests separate (pro?)
  34. 36. Potential for unwieldy test suites </li></ul>
  35. 37. The Building Blocks <ul><li>Assertions
  36. 38. Test Cases
  37. 39. Test Fixtures
  38. 40. Test Suites
  39. 41. Test Runners </li></ul>
  40. 42. Test Runners A test runner is a component which orchestrates the execution of tests and provides the outcome to the user. The runner may use a graphical interface, a textual interface, or return a special value to indicate the results of executing the tests. Source: Python documentation
  41. 43. Test Suites A test suite is a collection of test cases, test suites, or both. It is used to aggregate tests that should be executed together. Source: Python documentation
  42. 44. Test Fixtures A test fixture represents the preparation needed to perform one or more tests, and any associate cleanup actions. This may involve, for example, creating temporary or proxy databases, directories, or starting a server process. Source: Python documentation
  43. 45. Test Cases A test case is the smallest unit of testing. It checks for a specific response to a particular set of inputs. unittest provides a base class, TestCase, which may be used to create new test cases. Source: Python documentation
  44. 46. Assertions In computer programming, an assertion is a predicate (for example a true–false statement) placed in a program to indicate that the developer thinks that the predicate is always true at that place. Source: Wikipedia
  45. 47. import unittest class TestFoo(unittest.TestCase): def test_something(self): self.assertTrue('x' in 'xyz') if __name__ == '__main__': unittest.main()
  46. 48. import unittest class TestFoo(unittest.TestCase): def test_something(self): self.assertTrue('x' in 'xyz') if __name__ == '__main__': unittest.main() This calls the standard test runner, and gathers up all TestCase classes in the file.
  47. 49. import unittest class TestFoo(unittest.TestCase): def test_something(self): self.assertTrue('x' in 'xyz') if __name__ == '__main__': unittest.main() Test cases are subclasses of TestCase.
  48. 50. import unittest class TestFoo(unittest.TestCase): def test_something(self): self.assertTrue('x' in 'xyz') if __name__ == '__main__': unittest.main() And there's an assertion. Simples.
  49. 51. There are more types of assertion than assertTrue, otherwise things could get awkward.
  50. 52. Note: I'm still stuck with Python 2.4, 2.5 and 2.6! Python 2.7 and 3.x have lots of new assertions available.
  51. 53. assertTrue has a negative counterpart: assertTrue(x) assertFalse(x) ...which can aid readability, but isn't particularly exciting.
  52. 54. We can test for equality: assertEqual(x, y) Which is asserting that x == y Like assertTrue, it has a counterpart: assertNotEqual(x, y)
  53. 55. A personal favourite of mine is: fail(message) Which I use as a placeholder: fail(&quot;Test not implemented&quot;) fail(&quot;Good. You do run the tests&quot;)
  54. 56. Ah, yes... what happens if a test fails?
  55. 58. Speaking of messages, most assertions have an optional argument for a message. assertTrue(x, &quot;It lied&quot;) assertEqual(x, y, &quot;Blah blah&quot;) The message shows if the assertion fails.
  56. 59. Another important assertion is: assertRaises(Exception, foo.x, y) In newer versions of Python, this has a much cleaner syntax... with self.assertRaises(Exception): foo.x(y)
  57. 60. Keep tests simple Test one thing at a time
  58. 61. class TestFoo(unittest.TestCase): def test_double_one(self): foo = MyFoo() self.assertEqual(2, foo.double(1)) def test_double_two(self): foo = MyFoo() self.assertEqual(4, foo.double(2))
  59. 62. Did you spot the duplication? Time to introduce Test Fixtures...
  60. 63. Fixtures allow us to set up and tear down the environment of the tests. It also allows us to factor out any boilerplate and duplication.
  61. 64. class TestFoo(unittest.TestCase): def test_double_one(self): foo = MyFoo() self.assertEqual(2, foo.double(1)) def test_double_two(self): foo = MyFoo() self.assertEqual(4, foo.double(2)) Let's factor out the duplication...
  62. 65. class TestFoo(unittest.TestCase): def setUp(self): self.foo = MyFoo() def test_double_one(self): self.assertEqual(2, self.foo.double(1)) def test_double_two(self): self.assertEqual(4, self.foo.double(2))
  63. 66. class TestFoo(unittest.TestCase): def setUp(self): self.foo = MyFoo() def test_double_one(self): self.assertEqual(2, self.foo.double(1)) def test_double_two(self): self.assertEqual(4, self.foo.double(2)) setUp is called before every test in the test case.
  64. 67. setUp has a partner called tearDown which, if present, is called after every test. Ideal for cleaning up afterwards. def tearDown(self): self.foo.close()
  65. 68. Things to think about when testing...
  66. 69. Keep each test case focused on a particular unit of code Organise tests within the test case to check individual aspects of the code Helps keep the tests simple and maintainable
  67. 70. Multiple test cases can exist in a file. Ideally, break out the test cases into multiple files just as you would with your code.
  68. 71. If code is difficult to test: refactor or rewrite to make it easier. This also leads to simpler, more maintainable code.
  69. 72. When you write tests, you're adding confidence in the code There's always one more bug to find There's always one more test to write
  70. 73. If you find a bug in your code, write a test to replicate it before you implement a fix
  71. 74. Even a &quot;bad&quot; test gives value over no test. Bad tests can always be improved as we understand the problem / solution.
  72. 75. Code under test should be isolated whenever possible, to minimise external influence We can isolate code from things like databases, network connections, etc. through Fakes and Mocks
  73. 76. Fakes are dummy objects that do nothing except stand-in for something Mocks are more interesting. They can capture and respond to messages And now over to Michael...

×