From 2c5e9504d8902e8ad3e7e9323a872491c43d520a Mon Sep 17 00:00:00 2001 From: Antoine Pitrou Date: Sun, 20 Jan 2013 01:29:39 +0100 Subject: [PATCH] Remove cruft and circumvolutions from the unittest docs. (I would have preferred to post this for review but the bug tracker is pretty much dead at the moment) --- Doc/library/unittest.rst | 253 +++++++++------------------------------ 1 file changed, 56 insertions(+), 197 deletions(-) diff --git a/Doc/library/unittest.rst b/Doc/library/unittest.rst index 06f3d831fde..3476957f155 100644 --- a/Doc/library/unittest.rst +++ b/Doc/library/unittest.rst @@ -11,17 +11,14 @@ (If you are already familiar with the basic concepts of testing, you might want to skip to :ref:`the list of assert methods `.) -The Python unit testing framework, sometimes referred to as "PyUnit," is a -Python language version of JUnit, by Kent Beck and Erich Gamma. JUnit is, in -turn, a Java version of Kent's Smalltalk testing framework. Each is the de -facto standard unit testing framework for its respective language. +The :mod:`unittest` unit testing framework was originally inspired by JUnit +and has a similar flavor as major unit testing frameworks in other +languages. It supports test automation, sharing of setup and shutdown code +for tests, aggregation of tests into collections, and independence of the +tests from the reporting framework. -:mod:`unittest` supports test automation, sharing of setup and shutdown code for -tests, aggregation of tests into collections, and independence of the tests from -the reporting framework. The :mod:`unittest` module provides classes that make -it easy to support these qualities for a set of tests. - -To achieve this, :mod:`unittest` supports some important concepts: +To achieve this, :mod:`unittest` supports some important concepts in an +object-oriented way: test fixture A :dfn:`test fixture` represents the preparation needed to perform one or more @@ -30,7 +27,7 @@ test fixture process. test case - A :dfn:`test case` is the smallest unit of testing. It checks for a specific + A :dfn:`test case` is the individual unit of testing. It checks for a specific response to a particular set of inputs. :mod:`unittest` provides a base class, :class:`TestCase`, which may be used to create new test cases. @@ -44,43 +41,12 @@ test runner a textual interface, or return a special value to indicate the results of executing the tests. -The test case and test fixture concepts are supported through the -:class:`TestCase` and :class:`FunctionTestCase` classes; the former should be -used when creating new tests, and the latter can be used when integrating -existing test code with a :mod:`unittest`\ -driven framework. When building test -fixtures using :class:`TestCase`, the :meth:`~TestCase.setUp` and -:meth:`~TestCase.tearDown` methods can be overridden to provide initialization -and cleanup for the fixture. With :class:`FunctionTestCase`, existing functions -can be passed to the constructor for these purposes. When the test is run, the -fixture initialization is run first; if it succeeds, the cleanup method is run -after the test has been executed, regardless of the outcome of the test. Each -instance of the :class:`TestCase` will only be used to run a single test method, -so a new fixture is created for each test. - -Test suites are implemented by the :class:`TestSuite` class. This class allows -individual tests and test suites to be aggregated; when the suite is executed, -all tests added directly to the suite and in "child" test suites are run. - -A test runner is an object that provides a single method, -:meth:`~TestRunner.run`, which accepts a :class:`TestCase` or :class:`TestSuite` -object as a parameter, and returns a result object. The class -:class:`TestResult` is provided for use as the result object. :mod:`unittest` -provides the :class:`TextTestRunner` as an example test runner which reports -test results on the standard error stream by default. Alternate runners can be -implemented for other environments (such as graphical environments) without any -need to derive from a specific class. - .. seealso:: Module :mod:`doctest` Another test-support module with a very different flavor. - `unittest2: A backport of new unittest features for Python 2.4-2.6 `_ - Many new features were added to unittest in Python 2.7, including test - discovery. unittest2 allows you to use these features with earlier - versions of Python. - `Simple Smalltalk Testing: With Patterns `_ Kent Beck's original paper on testing frameworks using the pattern shared by :mod:`unittest`. @@ -89,7 +55,7 @@ need to derive from a specific class. Third-party unittest frameworks with a lighter-weight syntax for writing tests. For example, ``assert func(10) == 42``. - `The Python Testing Tools Taxonomy `_ + `The Python Testing Tools Taxonomy `_ An extensive list of Python testing tools including functional testing frameworks and mock object libraries. @@ -173,15 +139,8 @@ line, the above script produces an output that looks like this:: OK -Instead of :func:`unittest.main`, there are other ways to run the tests with a -finer level of control, less terse output, and no requirement to be run from the -command line. For example, the last two lines may be replaced with:: - - suite = unittest.TestLoader().loadTestsFromTestCase(TestSequenceFunctions) - unittest.TextTestRunner(verbosity=2).run(suite) - -Running the revised script from the interpreter or another script produces the -following output:: +Passing the ``-v`` option to your test script will instruct :func:`unittest.main` +to enable a higher level of verbosity, and produce the following output:: test_choice (__main__.TestSequenceFunctions) ... ok test_sample (__main__.TestSequenceFunctions) ... ok @@ -359,45 +318,30 @@ test cases are represented by :class:`unittest.TestCase` instances. To make your own test cases you must write subclasses of :class:`TestCase` or use :class:`FunctionTestCase`. -An instance of a :class:`TestCase`\ -derived class is an object that can -completely run a single test method, together with optional set-up and tidy-up -code. - The testing code of a :class:`TestCase` instance should be entirely self contained, such that it can be run either in isolation or in arbitrary combination with any number of other test cases. -The simplest :class:`TestCase` subclass will simply override the -:meth:`~TestCase.runTest` method in order to perform specific testing code:: +The simplest :class:`TestCase` subclass will simply implement a test method +(i.e. a method whose name starts with ``test``) in order to perform specific +testing code:: import unittest class DefaultWidgetSizeTestCase(unittest.TestCase): - def runTest(self): + def test_default_widget_size(self): widget = Widget('The widget') - self.assertEqual(widget.size(), (50, 50), 'incorrect default size') + self.assertEqual(widget.size(), (50, 50)) Note that in order to test something, we use one of the :meth:`assert\*` methods provided by the :class:`TestCase` base class. If the test fails, an exception will be raised, and :mod:`unittest` will identify the test case as a -:dfn:`failure`. Any other exceptions will be treated as :dfn:`errors`. This -helps you identify where the problem is: :dfn:`failures` are caused by incorrect -results - a 5 where you expected a 6. :dfn:`Errors` are caused by incorrect -code - e.g., a :exc:`TypeError` caused by an incorrect function call. +:dfn:`failure`. Any other exceptions will be treated as :dfn:`errors`. -The way to run a test case will be described later. For now, note that to -construct an instance of such a test case, we call its constructor without -arguments:: - - testCase = DefaultWidgetSizeTestCase() - -Now, such test cases can be numerous, and their set-up can be repetitive. In -the above case, constructing a :class:`Widget` in each of 100 Widget test case -subclasses would mean unsightly duplication. - -Luckily, we can factor out such set-up code by implementing a method called -:meth:`~TestCase.setUp`, which the testing framework will automatically call for -us when we run the test:: +Tests can be numerous, and their set-up can be repetitive. Luckily, we +can factor out set-up code by implementing a method called +:meth:`~TestCase.setUp`, which the testing framework will automatically +call for every single test we run:: import unittest @@ -405,23 +349,26 @@ us when we run the test:: def setUp(self): self.widget = Widget('The widget') - class DefaultWidgetSizeTestCase(SimpleWidgetTestCase): - def runTest(self): + def test_default_widget_size(self): self.assertEqual(self.widget.size(), (50,50), 'incorrect default size') - class WidgetResizeTestCase(SimpleWidgetTestCase): - def runTest(self): + def test_widget_resize(self): self.widget.resize(100,150) self.assertEqual(self.widget.size(), (100,150), 'wrong size after resize') +.. note:: + The order in which the various tests will be run is determined + by sorting the test method names with respect to the built-in + ordering for strings. + If the :meth:`~TestCase.setUp` method raises an exception while the test is -running, the framework will consider the test to have suffered an error, and the -:meth:`~TestCase.runTest` method will not be executed. +running, the framework will consider the test to have suffered an error, and +the test method will not be executed. Similarly, we can provide a :meth:`~TestCase.tearDown` method that tidies up -after the :meth:`~TestCase.runTest` method has been run:: +after the test method has been run:: import unittest @@ -431,59 +378,20 @@ after the :meth:`~TestCase.runTest` method has been run:: def tearDown(self): self.widget.dispose() - self.widget = None -If :meth:`~TestCase.setUp` succeeded, the :meth:`~TestCase.tearDown` method will -be run whether :meth:`~TestCase.runTest` succeeded or not. +If :meth:`~TestCase.setUp` succeeded, :meth:`~TestCase.tearDown` will be +run whether the test method succeeded or not. Such a working environment for the testing code is called a :dfn:`fixture`. -Often, many small test cases will use the same fixture. In this case, we would -end up subclassing :class:`SimpleWidgetTestCase` into many small one-method -classes such as :class:`DefaultWidgetSizeTestCase`. This is time-consuming and -discouraging, so in the same vein as JUnit, :mod:`unittest` provides a simpler -mechanism:: - - import unittest - - class WidgetTestCase(unittest.TestCase): - def setUp(self): - self.widget = Widget('The widget') - - def tearDown(self): - self.widget.dispose() - self.widget = None - - def test_default_size(self): - self.assertEqual(self.widget.size(), (50,50), - 'incorrect default size') - - def test_resize(self): - self.widget.resize(100,150) - self.assertEqual(self.widget.size(), (100,150), - 'wrong size after resize') - -Here we have not provided a :meth:`~TestCase.runTest` method, but have instead -provided two different test methods. Class instances will now each run one of -the :meth:`test_\*` methods, with ``self.widget`` created and destroyed -separately for each instance. When creating an instance we must specify the -test method it is to run. We do this by passing the method name in the -constructor:: - - defaultSizeTestCase = WidgetTestCase('test_default_size') - resizeTestCase = WidgetTestCase('test_resize') - Test case instances are grouped together according to the features they test. :mod:`unittest` provides a mechanism for this: the :dfn:`test suite`, -represented by :mod:`unittest`'s :class:`TestSuite` class:: +represented by :mod:`unittest`'s :class:`TestSuite` class. In most cases, +calling :func:`unittest.main` will do the right thing and collect all the +module's test cases for you, and then execute them. - widgetTestSuite = unittest.TestSuite() - widgetTestSuite.addTest(WidgetTestCase('test_default_size')) - widgetTestSuite.addTest(WidgetTestCase('test_resize')) - -For the ease of running tests, as we will see later, it is a good idea to -provide in each test module a callable object that returns a pre-built test -suite:: +However, should you want to customize the building of your test suite, +you can do it yourself:: def suite(): suite = unittest.TestSuite() @@ -491,37 +399,6 @@ suite:: suite.addTest(WidgetTestCase('test_resize')) return suite -or even:: - - def suite(): - tests = ['test_default_size', 'test_resize'] - - return unittest.TestSuite(map(WidgetTestCase, tests)) - -Since it is a common pattern to create a :class:`TestCase` subclass with many -similarly named test functions, :mod:`unittest` provides a :class:`TestLoader` -class that can be used to automate the process of creating a test suite and -populating it with individual tests. For example, :: - - suite = unittest.TestLoader().loadTestsFromTestCase(WidgetTestCase) - -will create a test suite that will run ``WidgetTestCase.test_default_size()`` and -``WidgetTestCase.test_resize``. :class:`TestLoader` uses the ``'test'`` method -name prefix to identify test methods automatically. - -Note that the order in which the various test cases will be run is -determined by sorting the test function names with respect to the -built-in ordering for strings. - -Often it is desirable to group suites of test cases together, so as to run tests -for the whole system at once. This is easy, since :class:`TestSuite` instances -can be added to a :class:`TestSuite` just as :class:`TestCase` instances can be -added to a :class:`TestSuite`:: - - suite1 = module1.TheTestSuite() - suite2 = module2.TheTestSuite() - alltests = unittest.TestSuite([suite1, suite2]) - You can place the definitions of test cases and test suites in the same modules as the code they are to test (such as :file:`widget.py`), but there are several advantages to placing the test code in a separate module, such as @@ -564,23 +441,13 @@ Given the following test function:: assert something.name is not None # ... -one can create an equivalent test case instance as follows:: - - testcase = unittest.FunctionTestCase(testSomething) - -If there are additional set-up and tear-down methods that should be called as -part of the test case's operation, they can also be provided like so:: +one can create an equivalent test case instance as follows, with optional +set-up and tear-down methods:: testcase = unittest.FunctionTestCase(testSomething, setUp=makeSomethingDB, tearDown=deleteSomethingDB) -To make migrating existing test suites easier, :mod:`unittest` supports tests -raising :exc:`AssertionError` to indicate test failure. However, it is -recommended that you use the explicit :meth:`TestCase.fail\*` and -:meth:`TestCase.assert\*` methods instead, as future versions of :mod:`unittest` -may treat :exc:`AssertionError` differently. - .. note:: Even though :class:`FunctionTestCase` can be used to quickly convert an @@ -704,32 +571,24 @@ Test cases .. class:: TestCase(methodName='runTest') - Instances of the :class:`TestCase` class represent the smallest testable units + Instances of the :class:`TestCase` class represent the logical test units in the :mod:`unittest` universe. This class is intended to be used as a base class, with specific tests being implemented by concrete subclasses. This class implements the interface needed by the test runner to allow it to drive the - test, and methods that the test code can use to check for and report various + tests, and methods that the test code can use to check for and report various kinds of failure. - Each instance of :class:`TestCase` will run a single test method: the method - named *methodName*. If you remember, we had an earlier example that went - something like this:: - - def suite(): - suite = unittest.TestSuite() - suite.addTest(WidgetTestCase('test_default_size')) - suite.addTest(WidgetTestCase('test_resize')) - return suite - - Here, we create two instances of :class:`WidgetTestCase`, each of which runs a - single test. + Each instance of :class:`TestCase` will run a single base method: the method + named *methodName*. However, the standard implementation of the default + *methodName*, ``runTest()``, will run every method starting with ``test`` + as an individual test, and count successes and failures accordingly. + Therefore, in most uses of :class:`TestCase`, you will neither change + the *methodName* nor reimplement the default ``runTest()`` method. .. versionchanged:: 3.2 - :class:`TestCase` can be instantiated successfully without providing a method - name. This makes it easier to experiment with :class:`TestCase` from the - interactive interpreter. - - *methodName* defaults to :meth:`runTest`. + :class:`TestCase` can be instantiated successfully without providing a + *methodName*. This makes it easier to experiment with :class:`TestCase` + from the interactive interpreter. :class:`TestCase` instances provide three groups of methods: one group used to run the test, another used by the test implementation to check conditions @@ -738,7 +597,6 @@ Test cases Methods in the first group (running the test) are: - .. method:: setUp() Method called to prepare the test fixture. This is called immediately @@ -790,10 +648,11 @@ Test cases .. method:: run(result=None) - Run the test, collecting the result into the test result object passed as - *result*. If *result* is omitted or ``None``, a temporary result - object is created (by calling the :meth:`defaultTestResult` method) and - used. The result object is returned to :meth:`run`'s caller. + Run the test, collecting the result into the :class:`TestResult` object + passed as *result*. If *result* is omitted or ``None``, a temporary + result object is created (by calling the :meth:`defaultTestResult` + method) and used. The result object is returned to :meth:`run`'s + caller. The same effect may be had by simply calling the :class:`TestCase` instance.