[Python-Dev] PEP: Consolidating names in the `unittest` module
Ben Finney
ben+python at benfinney.id.au
Tue Jul 15 15:58:02 CEST 2008
Significant updates include removing all reference to the
(already-resolved) new-style class issue, adding footnotes and
references, and a Rationale summary of discussion on both sides of the
divide for 'assert*' versus 'fail*' names.
:PEP: XXX
:Title: Consolidating names in the `unittest` module
:Version: 0.2
:Last-Modified: 2008-07-15
:Author: Ben Finney <ben+python at benfinney.id.au>
:Status: Draft
:Type: Standards Track
:Content-Type: test/x-rst
:Created: 2008-07-14
:Python-Version: 2.7, 3.1
:Post-History:
.. contents::
Abstract
========
This PEP proposes to consolidate the names that constitute the API of
the standard library `unittest` module, with the goal of removing
redundant names, and conforming with PEP 8.
Motivation
==========
The normal use case for the `unittest` module is to subclass its
classes, overriding and re-using its functios and methods. This draws
constant attention to the fact that the existing implementation fails
several current Python standards:
* It does not conform to PEP 8 [#PEP-8]_, requiring users to write
their own non-PEP-8 conformant names when overriding methods, and
encouraging extensions to further depart from PEP 8.
* It has many synonyms in its API, which goes against the Zen of
Python [#PEP-20]_ (specifically, that "there should be one, and
preferably only one, obvious way to do it").
Specification
=============
Remove obsolete names
---------------------
The following module attributes are not documented as part of the API
and are marked as obsolete in the implementation. They will be
removed.
* ``_makeLoader``
* ``getTestCaseNames``
* ``makeSuite``
* ``findTestCases``
Remove redundant names
----------------------
The following attribute names exist only as synonyms for other names.
They are to be removed, leaving only one name for each attribute in
the API.
``TestCase`` attributes
~~~~~~~~~~~~~~~~~~~~~~~
* ``assertEqual``
* ``assertEquals``
* ``assertNotEqual``
* ``assertNotEquals``
* ``assertAlmostEqual``
* ``assertAlmostEquals``
* ``assertNotAlmostEqual``
* ``assertNotAlmostEquals``
* ``assertRaises``
* ``assert_``
* ``assertTrue``
* ``assertFalse``
Conform API with PEP 8
----------------------
The following names are to be introduced, each replacing an existing
name, to make all names in the module conform with PEP 8 [#PEP-8]_.
Each name is shown with the existing name that it replaces.
Where function parameters are to be renamed also, they are shown.
Where function parameters are not to be renamed, they are elided with
the ellipse ("…") symbol.
Module attributes
~~~~~~~~~~~~~~~~~
``default_test_loader``
Replaces ``defaultTestLoader``
``TestResult`` attributes
~~~~~~~~~~~~~~~~~~~~~~~~~
``add_error(…)``
Replaces ``addError(…)``
``add_result(…)``
Replaces ``addResult(…)``
``add_success(…)``
Replaces ``addSuccess(…)``
``should_stop``
Replaces ``shouldStop``
``start_test(…)``
Replaces ``startTest(…)``
``stop_test(…)``
Replaces ``stopTest(…)``
``tests_run``
Replaces ``testsRun``
``was_successful(…)``
Replaces ``wasSuccessful(…)``
``TestCase`` attributes
~~~~~~~~~~~~~~~~~~~~~~~
``__init__(self, method_name='run_test')``
Replaces ``__init__(self, methodName='runTest')``
``_test_method_doc``
Replaces ``_testMethodDoc``
``_test_method_name``
Replaces ``_testMethodName``
``failure_exception``
Replaces ``failureException``
``count_test_cases(…)``
Replaces ``countTestCases(…)``
``default_test_result(…)``
Replaces ``defaultTestResult(…)``
``fail_if(…)``
Replaces ``failIf(…)``
``fail_if_almost_equal(…)``
Replaces ``failIfAlmostEqual(…)``
``fail_if_equal(…)``
Replaces ``failIfEqual(…)``
``fail_unless(…)``
Replaces ``failUnless(…)``
``fail_unless_almost_equal(…)``
Replaces ``failUnlessAlmostEqual(…)``
``fail_unless_equal(…)``
Replaces ``failUnlessEqual(…)``
``fail_unless_raises(exc_class, callable_obj, *args, **kwargs)``
Replaces ``failUnlessRaises(excClass, callableObj, *args, **kwargs)``
``run_test(…)``
Replaces ``runTest(…)``
``set_up(…)``
Replaces ``setUp(…)``
``short_description(…)``
Replaces ``shortDescription(…)``
``tear_down(…)``
Replaces ``tearDown(…)``
``FunctionTestCase`` attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``__init__(self, test_func, set_up, tear_down, description)``
Replaces ``__init__(self, testFunc, setUp, tearDown, description)``
``run_test(…)``
Replaces ``runTest(…)``
``set_up(…)``
Replaces ``setUp(…)``
``short_description(…)``
Replaces ``shortDescription(…)``
``tear_down(…)``
Replaces ``tearDown(…)``
``TestSuite`` attributes
~~~~~~~~~~~~~~~~~~~~~~~~
``add_test(…)``
Replaces ``addTest(…)``
``add_tests(…)``
Replaces ``addTests(…)``
``count_test_cases(…)``
Replaces ``countTestCases(…)``
``TestLoader`` attributes
~~~~~~~~~~~~~~~~~~~~~~~~~
``sort_test_methods_using``
Replaces ``sortTestMethodsUsing``
``suite_class``
Replaces ``suiteClass``
``test_method_prefix``
Replaces ``testMethodPrefix``
``get_test_case_names(self, test_case_class)``
Replaces ``getTestCaseNames(self, testCaseClass)``
``load_tests_from_module(…)``
Replaces ``loadTestsFromModule(…)``
``load_tests_from_name(…)``
Replaces ``loadTestsFromName(…)``
``load_tests_from_names(…)``
Replaces ``loadTestsFromNames(…)``
``load_tests_from_test_case(self, test_case_class)``
Replaces ``loadTestsFromTestCase(self, testCaseClass)``
``_TextTestResult`` attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``show_all``
Replaces ``showAll``
``add_error(…)``
Replaces ``addError(…)``
``add_failure(…)``
Replaces ``addFailure(…)``
``add_success(…)``
Replaces ``addSuccess(…)``
``get_description(…)``
Replaces ``getDescription(…)``
``print_error_list(…)``
Replaces ``printErrorList(…)``
``print_errors(…)``
Replaces ``printErrors(…)``
``start_test(…)``
Replaces ``startTest(…)``
``TextTestRunner`` attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``_make_result(…)``
Replaces ``_makeResult(…)``
``TestProgram`` attributes
~~~~~~~~~~~~~~~~~~~~~~~~~~
``__init__(self, module, default_test, argv, test_runner, test_loader)``
Replaces ``__init__(self, module, defaultTest, argv, testRunner, testLoader)``
``create_tests(…)``
Replaces ``createTests(…)``
``parse_args(…)``
Replaces ``parseArgs(…)``
``run_tests(…)``
Replaces ``runTests(…)``
``usage_exit(…)``
Replaces ``usageExit(…)``
Rationale
=========
Redundant names
---------------
The current API, with two or in some cases three different names
referencing exactly the same function, leads to an overbroad and
redundant API that violates PEP 20 [#PEP-20]_ ("there should be one,
and preferably only one, obvious way to do it").
Removal of ``assert*`` names
----------------------------
While there is consensus support to `remove redundant names`_ for the
``TestCase`` test methods, the issue of which set of names should be
retained is controversial.
Arguments in favour of retaining only the ``assert*`` names:
* BDFL preference: The BDFL has stated [#vanrossum-1]_ a preference
for the ``assert*`` names.
* Precedent: The Python standard library currently uses the
``assert*`` names by a roughly 8:1 majority over the ``fail*``
names. (Counting unit tests in the py3k tree at 2008-07-15
[#pitrou-1]_.)
An ad-hoc sampling of other projects that use `unittest` also
demonstrates strong preference for use of the ``assert*`` names
[#bennetts-1]_.
* Positive admonition: The ``assert*`` names state the intent of how
the code under test *should* behave, while the ``fail*`` names are
phrased in terms of how the code *should not* behave.
Arguments in favour of retaining only the ``fail*`` names:
* Explicit is better than implicit: The ``fail*`` names state *what
the function will do* explicitly: fail the test. With the
``assert*`` names, the action to be taken is only implicit.
* Avoid false implication: The test methods do not have any necessary
connection with the built-in ``assert`` statement. Even the
exception raised, while it defaults to ``AssertionException``, is
explicitly customisable via the documented ``failure_exception``
attribute. Choosing the ``fail*`` names avoids the false association
with either of these.
This is exacerbated by the plain-boolean test using a name of
``assert_`` (with a trailing underscore) to avoid a name collision
with the built-in ``assert`` statement. The corresponding
``fail_if`` name has no such issue.
PEP 8 names
-----------
Although `unittest` (and its predecessor `PyUnit`) are intended to be
familiar to users of other xUnit interfaces, there is no attempt at
direct API compatibility since the only code that Python's `unittest`
interfaces with is other Python code. The module is in the standard
library and its names should all conform with PEP 8 [#PEP-8]_.
Backwards Compatibility
=======================
The names to be obsoleted should be deprecated and removed according
to the schedule for modules in PEP 4 [#PEP-4]_.
While deprecated, use of the deprecated attributes should raise a
``DeprecationWarning``, with a message stating which replacement name
should be used.
Reference Implementation
========================
None yet.
Copyright
=========
This document is hereby placed in the public domain by its author.
.. [#PEP-4] http://www.python.org/dev/peps/pep-0004
.. [#PEP-8] http://www.python.org/dev/peps/pep-0008
.. [#PEP-20] http://www.python.org/dev/peps/pep-0020
.. [#vanrossum-1] http://mail.python.org/pipermail/python-dev/2008-April/078485.html
.. [#pitrou-1] http://mail.python.org/pipermail/python-dev/2008-July/081090.html
.. [#bennetts-1] http://mail.python.org/pipermail/python-dev/2008-July/081141.html
..
Local Variables:
mode: rst
coding: utf-8
End:
vim: filetype=rst :
More information about the Python-Dev
mailing list