unittest: An under-appreciated gem Andrew Bennetts 3rd December, - - PowerPoint PPT Presentation

unittest an under appreciated gem
SMART_READER_LITE
LIVE PREVIEW

unittest: An under-appreciated gem Andrew Bennetts 3rd December, - - PowerPoint PPT Presentation

Introduction Why unittest is good Extending unittest Wrapping up unittest: An under-appreciated gem Andrew Bennetts 3rd December, 2008 Andrew Bennetts unittest: An under-appreciated gem Introduction What I hope you get out of this talk Why


slide-1
SLIDE 1

Introduction Why unittest is good Extending unittest Wrapping up

unittest: An under-appreciated gem

Andrew Bennetts 3rd December, 2008

Andrew Bennetts unittest: An under-appreciated gem

slide-2
SLIDE 2

Introduction Why unittest is good Extending unittest Wrapping up What I hope you get out of this talk What makes a good unit test suite? unittest basics

What I hope you get out of this talk

In no particular order. . .

Andrew Bennetts unittest: An under-appreciated gem

slide-3
SLIDE 3

Introduction Why unittest is good Extending unittest Wrapping up What I hope you get out of this talk What makes a good unit test suite? unittest basics

What I hope you get out of this talk

In no particular order. . .

◮ Why I think Python’s unittest module is good

Andrew Bennetts unittest: An under-appreciated gem

slide-4
SLIDE 4

Introduction Why unittest is good Extending unittest Wrapping up What I hope you get out of this talk What makes a good unit test suite? unittest basics

What I hope you get out of this talk

In no particular order. . .

◮ Why I think Python’s unittest module is good ◮ A new appreciation of what’s possible with Python’s unittest

module

Andrew Bennetts unittest: An under-appreciated gem

slide-5
SLIDE 5

Introduction Why unittest is good Extending unittest Wrapping up What I hope you get out of this talk What makes a good unit test suite? unittest basics

What I hope you get out of this talk

In no particular order. . .

◮ Why I think Python’s unittest module is good ◮ A new appreciation of what’s possible with Python’s unittest

module

◮ Some general insights into good unit tests

Andrew Bennetts unittest: An under-appreciated gem

slide-6
SLIDE 6

Introduction Why unittest is good Extending unittest Wrapping up What I hope you get out of this talk What makes a good unit test suite? unittest basics

What I hope you get out of this talk

In no particular order. . .

◮ Why I think Python’s unittest module is good ◮ A new appreciation of what’s possible with Python’s unittest

module

◮ Some general insights into good unit tests

Andrew Bennetts unittest: An under-appreciated gem

slide-7
SLIDE 7

Introduction Why unittest is good Extending unittest Wrapping up What I hope you get out of this talk What makes a good unit test suite? unittest basics

Disclaimer

I’m not doing a comparison with other test frameworks/tools (Nose, py.test, doctest, insert your favorite here, . . . )

Andrew Bennetts unittest: An under-appreciated gem

slide-8
SLIDE 8

Introduction Why unittest is good Extending unittest Wrapping up What I hope you get out of this talk What makes a good unit test suite? unittest basics

What makes a good unit test suite?

Andrew Bennetts unittest: An under-appreciated gem

slide-9
SLIDE 9

Introduction Why unittest is good Extending unittest Wrapping up What I hope you get out of this talk What makes a good unit test suite? unittest basics

What makes a good unit test suite?

Readable: intent of each test is clear, implementation is clear Reliable: only fails when it should, only passes when it should Usable: easy to run, fast to run, easy to debug (if necessary!)

Andrew Bennetts unittest: An under-appreciated gem

slide-10
SLIDE 10

Introduction Why unittest is good Extending unittest Wrapping up What I hope you get out of this talk What makes a good unit test suite? unittest basics

unittest basics

Here’s a very quick overview of unittest.

Andrew Bennetts unittest: An under-appreciated gem

slide-11
SLIDE 11

Introduction Why unittest is good Extending unittest Wrapping up What I hope you get out of this talk What makes a good unit test suite? unittest basics

unittest basics — toy example

class TestFrobnicator(unittest.TestCase): def setUp(self): self.frobnicator = Frobnicator() self.frobnicator.initialise() def test frob one word(self): input = ”word”

  • utput = self.frobnicator.frob(input)

self.assertEqual(”frob”, output) def test frob two words(self): input = ”two words”

  • utput = self.frobnicator.frob(input)

self.assertEqual(”frob frob”, output)

Andrew Bennetts unittest: An under-appreciated gem

slide-12
SLIDE 12

Introduction Why unittest is good Extending unittest Wrapping up What I hope you get out of this talk What makes a good unit test suite? unittest basics

unittest basics — toy example

class TestFrobnicator(unittest.TestCase): def setUp(self): self.frobnicator = Frobnicator() self.frobnicator.initialise() def test frob one word(self): input = ”word”

  • utput = self.frobnicator.frob(input)

self.assertEqual(”frob”, output) def test frob two words(self): input = ”two words”

  • utput = self.frobnicator.frob(input)

self.assertEqual(”frob frob”, output)

Andrew Bennetts unittest: An under-appreciated gem

slide-13
SLIDE 13

Introduction Why unittest is good Extending unittest Wrapping up What I hope you get out of this talk What makes a good unit test suite? unittest basics

unittest basics — toy example

class TestFrobnicator(unittest.TestCase): def setUp(self): self.frobnicator = Frobnicator() self.frobnicator.initialise() def test frob one word(self): input = ”word”

  • utput = self.frobnicator.frob(input)

self.assertEqual(”frob”, output) def test frob two words(self): input = ”two words”

  • utput = self.frobnicator.frob(input)

self.assertEqual(”frob frob”, output)

Andrew Bennetts unittest: An under-appreciated gem

slide-14
SLIDE 14

Introduction Why unittest is good Extending unittest Wrapping up What I hope you get out of this talk What makes a good unit test suite? unittest basics

unittest basics — toy example

class TestFrobnicator(unittest.TestCase): def setUp(self): self.frobnicator = Frobnicator() self.frobnicator.initialise() def test frob one word(self): input = ”word”

  • utput = self.frobnicator.frob(input)

self.assertEqual(”frob”, output) def test frob two words(self): input = ”two words”

  • utput = self.frobnicator.frob(input)

self.assertEqual(”frob frob”, output)

Andrew Bennetts unittest: An under-appreciated gem

slide-15
SLIDE 15

Introduction Why unittest is good Extending unittest Wrapping up What I hope you get out of this talk What makes a good unit test suite? unittest basics

unittest basics — TestCase

Key fact: A single unit test is represented by a TestCase instance.

Andrew Bennetts unittest: An under-appreciated gem

slide-16
SLIDE 16

Introduction Why unittest is good Extending unittest Wrapping up What I hope you get out of this talk What makes a good unit test suite? unittest basics

unittest basics — TestCase

Key fact: A single unit test is represented by a TestCase instance. TestCase instances have a run method that will:

◮ run setUp ◮ run the test ◮ run tearDown ◮ report the outcome to a TestResult object

They also provide assertion methods like assertEquals.

Andrew Bennetts unittest: An under-appreciated gem

slide-17
SLIDE 17

Introduction Why unittest is good Extending unittest Wrapping up What I hope you get out of this talk What makes a good unit test suite? unittest basics

unittest basics — runners, loaders, results

Other major components: TestResult: object that can record details of a success or failure. TestLoader: turns test methods in TestCase subclasses into TestCase instances. TestRunner: glues everything together.

Andrew Bennetts unittest: An under-appreciated gem

slide-18
SLIDE 18

Introduction Why unittest is good Extending unittest Wrapping up It’s the Standard Structure Extensibility

Why unittest is good

Andrew Bennetts unittest: An under-appreciated gem

slide-19
SLIDE 19

Introduction Why unittest is good Extending unittest Wrapping up It’s the Standard Structure Extensibility

It’s the Standard

Andrew Bennetts unittest: An under-appreciated gem

slide-20
SLIDE 20

Introduction Why unittest is good Extending unittest Wrapping up It’s the Standard Structure Extensibility

It’s the Standard

It’s in the standard library.

◮ It’s always available. ◮ Most other test frameworks interoperate with it. ◮ Practically every Python programmer is at least minimally

familiar with it.

Andrew Bennetts unittest: An under-appreciated gem

slide-21
SLIDE 21

Introduction Why unittest is good Extending unittest Wrapping up It’s the Standard Structure Extensibility

It’s the Standard

It’s an implementation of xUnit.

◮ Proven design. ◮ Practically every non-Python programmer is at least minimally

familiar with it.

Andrew Bennetts unittest: An under-appreciated gem

slide-22
SLIDE 22

Introduction Why unittest is good Extending unittest Wrapping up It’s the Standard Structure Extensibility

Structure

Andrew Bennetts unittest: An under-appreciated gem

slide-23
SLIDE 23

Introduction Why unittest is good Extending unittest Wrapping up It’s the Standard Structure Extensibility

Structure — Isolation

When run, each test has its own TestCase instance. So by default, tests are isolated from each other.

Andrew Bennetts unittest: An under-appreciated gem

slide-24
SLIDE 24

Introduction Why unittest is good Extending unittest Wrapping up It’s the Standard Structure Extensibility

Structure — Reusable fixture definition

Each TestCase has a setUp and tearDown method. This makes it easy to reuse a test fixture definition between multiple tests.

Andrew Bennetts unittest: An under-appreciated gem

slide-25
SLIDE 25

Introduction Why unittest is good Extending unittest Wrapping up It’s the Standard Structure Extensibility

Structure — More code reuse

TestCases naturally group tests with common needs.

Andrew Bennetts unittest: An under-appreciated gem

slide-26
SLIDE 26

Introduction Why unittest is good Extending unittest Wrapping up It’s the Standard Structure Extensibility

Structure — More code reuse

TestCases naturally group tests with common needs. So any domain-specific test helpers you add (e.g. an assertUserHasPermission method) have a natural home. setUp and tearDown methods are built-in examples of this.

Andrew Bennetts unittest: An under-appreciated gem

slide-27
SLIDE 27

Introduction Why unittest is good Extending unittest Wrapping up It’s the Standard Structure Extensibility

Structure — Naming

Tests have explicit names. This allows clear reporting of exactly which tests are failing, and a way to run individual tests rather than the whole suite.

Andrew Bennetts unittest: An under-appreciated gem

slide-28
SLIDE 28

Introduction Why unittest is good Extending unittest Wrapping up It’s the Standard Structure Extensibility

Extensibility

Andrew Bennetts unittest: An under-appreciated gem

slide-29
SLIDE 29

Introduction Why unittest is good Extending unittest Wrapping up It’s the Standard Structure Extensibility

Extensibility

unittest is pretty easy to extend.

Andrew Bennetts unittest: An under-appreciated gem

slide-30
SLIDE 30

Introduction Why unittest is good Extending unittest Wrapping up It’s the Standard Structure Extensibility

Extensibility

unittest is pretty easy to extend. (And because unittest is the standard, your unittest-compatible extensions have an ok chance of working with other frameworks.)

Andrew Bennetts unittest: An under-appreciated gem

slide-31
SLIDE 31

Introduction Why unittest is good Extending unittest Wrapping up Introduction Examples Some Libraries

Extending unittest

Andrew Bennetts unittest: An under-appreciated gem

slide-32
SLIDE 32

Introduction Why unittest is good Extending unittest Wrapping up Introduction Examples Some Libraries

Introduction

Here’s some real world unittest extensions.

Andrew Bennetts unittest: An under-appreciated gem

slide-33
SLIDE 33

Introduction Why unittest is good Extending unittest Wrapping up Introduction Examples Some Libraries

addCleanup

addCleanup is a robust way to arrange for a cleanup function to be called before tearDown. This is a powerful alternative to putting cleanup logic in a try/finally block or tearDown method. For example: def test foo(self): foo.lock() self.addCleanup(foo.unlock) # etc...

Andrew Bennetts unittest: An under-appreciated gem

slide-34
SLIDE 34

Introduction Why unittest is good Extending unittest Wrapping up Introduction Examples Some Libraries

requireFeature

Bazaar has tests for how it handles symlinks, but Windows doesn’t support symlinks. Bazaar extended the standard TestCase class to allow a test to do: class TestFileRenaming(TestCase): test needs features = [SymlinkFeature] . . .

Andrew Bennetts unittest: An under-appreciated gem

slide-35
SLIDE 35

Introduction Why unittest is good Extending unittest Wrapping up Introduction Examples Some Libraries

requireFeature

Alternatively, individual test methods can call self.requireFeature(SymlinkFeature)

Andrew Bennetts unittest: An under-appreciated gem

slide-36
SLIDE 36

Introduction Why unittest is good Extending unittest Wrapping up Introduction Examples Some Libraries

requireFeature

A feature is easy to define: class SymlinkFeature(Feature): def probe(self): return hasattr(os, ’symlink’) def feature name(self): return ’symlinks’ SymlinkFeature = SymlinkFeature()

Andrew Bennetts unittest: An under-appreciated gem

slide-37
SLIDE 37

Introduction Why unittest is good Extending unittest Wrapping up Introduction Examples Some Libraries

requireFeature

A feature is easy to define: class SymlinkFeature(Feature): def probe(self): return hasattr(os, ’symlink’) def feature name(self): return ’symlinks’ SymlinkFeature = SymlinkFeature()

Andrew Bennetts unittest: An under-appreciated gem

slide-38
SLIDE 38

Introduction Why unittest is good Extending unittest Wrapping up Introduction Examples Some Libraries

Test parameterisation

Often a test is applicable to multiple scenarios. multiply test suite by scenarios is a function that takes a test suite and list of scenarios.

Andrew Bennetts unittest: An under-appreciated gem

slide-39
SLIDE 39

Introduction Why unittest is good Extending unittest Wrapping up Introduction Examples Some Libraries

Test parameterisation — example

Simplified test case example based on a real test case from Twisted: def load tests(standard tests, module, loader): tests = testtools.multiply test suite by scenarios( standard tests, Scenario(’LineReceiver’, lineReceiverClass=LineReceiver), Scenario(’LineOnlyReceiver’, lineReceiverClass=LineOnlyReceiver)) return unittest.TestSuite(tests)

Andrew Bennetts unittest: An under-appreciated gem

slide-40
SLIDE 40

Introduction Why unittest is good Extending unittest Wrapping up Introduction Examples Some Libraries

Test parameterisation — example

class LineReceiverTests(TestCase): def setUp(self): self.lineReceiver = self.makeLineReceiver( self.scenario.lineReceiverClass) def testLongLine(self): self.lineReceiver.MAX LENGTH = 5 self.lineReceiver.dataReceived(’123456\n789\n’) . . .

Andrew Bennetts unittest: An under-appreciated gem

slide-41
SLIDE 41

Introduction Why unittest is good Extending unittest Wrapping up Introduction Examples Some Libraries

Test parameterisation — example

That suite will contain two tests built from testLongLine:

◮ LineReceiverTests.testLongLine(LineReceiver) ◮ LineReceiverTests.testLongLine(LineOnlyReceiver)

Andrew Bennetts unittest: An under-appreciated gem

slide-42
SLIDE 42

Introduction Why unittest is good Extending unittest Wrapping up Introduction Examples Some Libraries

Custom test loaders

Many projects do this. e.g. in Bazaar modules can provide a load tests function that can return a customised TestSuite. For example, to return a suite that runs every test twice, you could do: def load tests(standard tests, module, loader): result = loader.suiteClass() for test in testtools.iter suite tests(standard tests): result.addTests([test, test]) return result

Andrew Bennetts unittest: An under-appreciated gem

slide-43
SLIDE 43

Introduction Why unittest is good Extending unittest Wrapping up Introduction Examples Some Libraries

Custom test loaders

Many projects do this. e.g. in Bazaar modules can provide a load tests function that can return a customised TestSuite. For example, to return a suite that runs every test twice, you could do: def load tests(standard tests, module, loader): result = loader.suiteClass() for test in testtools.iter suite tests(standard tests): result.addTests([test, test]) return result

Andrew Bennetts unittest: An under-appreciated gem

slide-44
SLIDE 44

Introduction Why unittest is good Extending unittest Wrapping up Introduction Examples Some Libraries

Some Libraries

Just quickly, a couple of libraries worth knowing about.

Andrew Bennetts unittest: An under-appreciated gem

slide-45
SLIDE 45

Introduction Why unittest is good Extending unittest Wrapping up Introduction Examples Some Libraries

Testtools

Miscellaneous extensions to unittest extracted from test suites of Twisted, Bazaar, etc. Maintained by Jonathan Lange — he’s here at OSDC, so find him and say hello. And give him patches to make it even better! https://launchpad.net/testtools

Andrew Bennetts unittest: An under-appreciated gem

slide-46
SLIDE 46

Introduction Why unittest is good Extending unittest Wrapping up Introduction Examples Some Libraries

SubUnit

SubUnit is a library for running unit tests in separate processes to support test isolation. Includes an IsolatedTestCase class that spawns a subprocess to run the test method. https://launchpad.net/subunit

Andrew Bennetts unittest: An under-appreciated gem

slide-47
SLIDE 47

Introduction Why unittest is good Extending unittest Wrapping up Introduction Examples Some Libraries

testresources

testresources is a library to manage the initialisation and lifetime of expensive test fixtures. e.g. databases used by a test suite often only need to be constructed once but standard test isolation causes them to be constructed for every fixture. testresources can manage that resource for you. https://launchpad.net/testresources

Andrew Bennetts unittest: An under-appreciated gem

slide-48
SLIDE 48

Introduction Why unittest is good Extending unittest Wrapping up Some bits of unittest suck Why I like unittest Things to remember

Wrapping up

Andrew Bennetts unittest: An under-appreciated gem

slide-49
SLIDE 49

Introduction Why unittest is good Extending unittest Wrapping up Some bits of unittest suck Why I like unittest Things to remember

Some bits of unittest suck

Andrew Bennetts unittest: An under-appreciated gem

slide-50
SLIDE 50

Introduction Why unittest is good Extending unittest Wrapping up Some bits of unittest suck Why I like unittest Things to remember

Some bits of unittest suck

◮ No standard tool for loading and invoking a test suite.

Andrew Bennetts unittest: An under-appreciated gem

slide-51
SLIDE 51

Introduction Why unittest is good Extending unittest Wrapping up Some bits of unittest suck Why I like unittest Things to remember

Some bits of unittest suck

◮ No standard tool for loading and invoking a test suite. ◮ Parts of the API could be better.

Andrew Bennetts unittest: An under-appreciated gem

slide-52
SLIDE 52

Introduction Why unittest is good Extending unittest Wrapping up Some bits of unittest suck Why I like unittest Things to remember

Some bits of unittest suck

◮ No standard tool for loading and invoking a test suite. ◮ Parts of the API could be better. ◮ The set of built-in assertions is a bit small.

Andrew Bennetts unittest: An under-appreciated gem

slide-53
SLIDE 53

Introduction Why unittest is good Extending unittest Wrapping up Some bits of unittest suck Why I like unittest Things to remember

Some bits of unittest suck

◮ No standard tool for loading and invoking a test suite. ◮ Parts of the API could be better. ◮ The set of built-in assertions is a bit small. ◮ The documentation of it that comes with Python could be

better.

Andrew Bennetts unittest: An under-appreciated gem

slide-54
SLIDE 54

Introduction Why unittest is good Extending unittest Wrapping up Some bits of unittest suck Why I like unittest Things to remember

But it doesn’t suck badly

unittest is fundamentally quite capable. Its shortcomings are pretty easy address with extensions...

Andrew Bennetts unittest: An under-appreciated gem

slide-55
SLIDE 55

Introduction Why unittest is good Extending unittest Wrapping up Some bits of unittest suck Why I like unittest Things to remember

But it doesn’t suck badly

unittest is fundamentally quite capable. Its shortcomings are pretty easy address with extensions... It would be great to get some of these extensions into standard unittest!

Andrew Bennetts unittest: An under-appreciated gem

slide-56
SLIDE 56

Introduction Why unittest is good Extending unittest Wrapping up Some bits of unittest suck Why I like unittest Things to remember

Why I like unittest

It’s standard. It encourages good unit test structure (IMO). It’s flexible enough to let me do what I need.

Andrew Bennetts unittest: An under-appreciated gem

slide-57
SLIDE 57

Introduction Why unittest is good Extending unittest Wrapping up Some bits of unittest suck Why I like unittest Things to remember

Things to remember

unittest is in the Python standard library, and it’s actually pretty good! There’s a bunch of excellent extensions you should know about if you are writing unittest code:

◮ PyUnit friends: https://launchpad.net/pyunit-friends ◮ Testtools: https://launchpad.net/testtools

Andrew Bennetts unittest: An under-appreciated gem

slide-58
SLIDE 58

Introduction Why unittest is good Extending unittest Wrapping up Some bits of unittest suck Why I like unittest Things to remember

Questions?

Questions?

Andrew Bennetts unittest: An under-appreciated gem