PEP: Consolidating names and classes in the `unittest` module
:PEP: XXX :Title: Consolidating names and classes in the `unittest` module :Version: 0.0 :Last-Modified: 2008-07-14 :Author: Ben Finney <ben+python@benfinney.id.au> :Status: Draft :Type: Standards Track :Content-Type: test/x-rst :Created: 2008-07-14 :Post-History: .. contents:: Abstract ======== This PEP proposes to consolidate the names and classes that constitute the API of the standard library `unittest` module, with the goal of removing redundant names, conforming with PEP 8, and eliminating classic classes. 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 use new-style classes, preventing e.g. straightforward use of ``super`` for calling the fixture set-up and tear-down methods. * It does not conform to 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 (specifically, that "there should be one, and preferably only one, obvious way to do it"). Specification ============= Use new-style classes throughout -------------------------------- The following classes will inherit explicitly from the built-in `object` type, to make all classes in the module part of the new-style type hierarchy. * ``TestResult`` * ``TestCase`` * ``TestSuite`` * ``TestLoader`` * ``_WritelnDecorator`` * ``TextTestRunner`` * ``TestProgram`` Remove obsolete names --------------------- The following 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. 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 ~~~~~~~~~~~~~~~~~ ``_make_loader(prefix, sort_using, suite_class)`` Replaces ``_makeLoader (prefix, sortUsing, suiteClass)`` ``find_test_cases(module, prefix, sort_using, suite_class)`` Replaces ``findTestCases(module, prefix, sortUsing, suiteClass)`` ``get_test_case_names(test_case_class, prefix, sort_using)`` Replaces ``getTestCaseNames(testCaseClass, prefix, sortUsing)`` ``make_suite(test_case_class, prefix, sort_using, suite_class)`` Replaces ``makeSuite(testCaseClass, prefix, sortUsing, suiteClass)`` ``default_test_loader`` Replaces ``defaultTestLoader`` ``TestResult`` atributes ~~~~~~~~~~~~~~~~~~~~~~~~ ``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 ========= New-style classes ----------------- As a standard library module, `unittest` should not define any classic classes. 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 ("there should be one, and preferably only one, obvious way to do it"). 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. Backwards Compatibility ======================= The names to be obsoleted should be deprecated and removed according to the schedule for modules in 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. .. Local Variables: mode: rst coding: utf-8 End: vim: filetype=rst :
On Mon, Jul 14, 2008 at 8:25 AM, Ben Finney <ben+python@benfinney.id.au> wrote:
Use new-style classes throughout --------------------------------
The following classes will inherit explicitly from the built-in `object` type, to make all classes in the module part of the new-style type hierarchy.
* ``TestResult`` * ``TestCase`` * ``TestSuite`` * ``TestLoader`` * ``_WritelnDecorator`` * ``TextTestRunner`` * ``TestProgram``
They already do. __metaclass__ = type is found in unittest.py. -- Cheers, Benjamin Peterson "There's no place like 127.0.0.1."
"Benjamin Peterson" <musiccomposition@gmail.com> writes:
On Mon, Jul 14, 2008 at 8:25 AM, Ben Finney <ben+python@benfinney.id.au> wrote:
Use new-style classes throughout --------------------------------
The following classes will inherit explicitly from the built-in `object` type, to make all classes in the module part of the new-style type hierarchy.
* ``TestResult`` * ``TestCase`` * ``TestSuite`` * ``TestLoader`` * ``_WritelnDecorator`` * ``TextTestRunner`` * ``TestProgram``
They already do. __metaclass__ = type is found in unittest.py.
Not in the copy I have. Is that in 3.x only, or in 2.x also? -- \ “I love to go down to the schoolyard and watch all the little | `\ children jump up and down and run around yelling and screaming. | _o__) They don't know I'm only using blanks.” —Emo Philips | Ben Finney
On Mon, Jul 14, 2008 at 6:18 PM, Ben Finney <ben+python@benfinney.id.au> wrote:
"Benjamin Peterson" <musiccomposition@gmail.com> writes:
On Mon, Jul 14, 2008 at 8:25 AM, Ben Finney <ben+python@benfinney.id.au> wrote:
Use new-style classes throughout --------------------------------
The following classes will inherit explicitly from the built-in `object` type, to make all classes in the module part of the new-style type hierarchy.
* ``TestResult`` * ``TestCase`` * ``TestSuite`` * ``TestLoader`` * ``_WritelnDecorator`` * ``TextTestRunner`` * ``TestProgram``
They already do. __metaclass__ = type is found in unittest.py.
Not in the copy I have. Is that in 3.x only, or in 2.x also?
Python 2.5.2 (r252:60911, Feb 22 2008, 07:57:53) [GCC 4.0.1 (Apple Computer, Inc. build 5363)] on darwin Type "help", "copyright", "credits" or "license" for more information.
import unittest isinstance(unittest.TestCase, object) True
-- Cheers, Benjamin Peterson "There's no place like 127.0.0.1."
Benjamin Peterson wrote:
On Mon, Jul 14, 2008 at 6:18 PM, Ben Finney <ben+python@benfinney.id.au> wrote:
"Benjamin Peterson" <musiccomposition@gmail.com> writes:
On Mon, Jul 14, 2008 at 8:25 AM, Ben Finney <ben+python@benfinney.id.au> wrote:
Use new-style classes throughout --------------------------------
The following classes will inherit explicitly from the built-in `object` type, to make all classes in the module part of the new-style type hierarchy.
* ``TestResult`` * ``TestCase`` * ``TestSuite`` * ``TestLoader`` * ``_WritelnDecorator`` * ``TextTestRunner`` * ``TestProgram``
They already do. __metaclass__ = type is found in unittest.py.
Not in the copy I have. Is that in 3.x only, or in 2.x also?
Python 2.5.2 (r252:60911, Feb 22 2008, 07:57:53) [GCC 4.0.1 (Apple Computer, Inc. build 5363)] on darwin Type "help", "copyright", "credits" or "license" for more information.
import unittest isinstance(unittest.TestCase, object)
True
That proves nothing: Python 2.5.2 (r252:60911, Feb 22 2008, 07:57:53) [GCC 4.0.1 (Apple Computer, Inc. build 5363)] on darwin Type "help", "copyright", "credits" or "license" for more information.
class x: pass ... isinstance(x, object) True isinstance(x, type) False type(x) <type 'classobj'>
-- http://www.ironpythoninaction.com/ http://www.voidspace.org.uk/ http://www.trypython.org/ http://www.ironpython.info/ http://www.theotherdelia.co.uk/ http://www.resolverhacks.net/
Michael Foord wrote:
Benjamin Peterson wrote:
On Mon, Jul 14, 2008 at 6:18 PM, Ben Finney <ben+python@benfinney.id.au> wrote:
"Benjamin Peterson" <musiccomposition@gmail.com> writes:
On Mon, Jul 14, 2008 at 8:25 AM, Ben Finney <ben+python@benfinney.id.au> wrote:
Use new-style classes throughout --------------------------------
The following classes will inherit explicitly from the built-in `object` type, to make all classes in the module part of the new-style type hierarchy.
[snip]
They already do. __metaclass__ = type is found in unittest.py.
Not in the copy I have. Is that in 3.x only, or in 2.x also?
Python 2.5.2 (r252:60911, Feb 22 2008, 07:57:53) [GCC 4.0.1 (Apple Computer, Inc. build 5363)] on darwin Type "help", "copyright", "credits" or "license" for more information.
import unittest isinstance(unittest.TestCase, object)
True
That proves nothing:
Python 2.5.2 (r252:60911, Feb 22 2008, 07:57:53) [GCC 4.0.1 (Apple Computer, Inc. build 5363)] on darwin Type "help", "copyright", "credits" or "license" for more information.
class x: pass ... isinstance(x, object) True isinstance(x, type) False type(x) <type 'classobj'>
While your retort is accurate, I think it's unintentionally deceptive, because you didn't finish your thought.. Benjamin is actually still correct: Python 2.5.1 (r251:54863, Apr 18 2007, 08:51:08) [MSC v.1310 32 bit (Intel)] on win32 Type "help", "copyright", "credits" or "license" for more information.
import unittest isinstance(unittest.TestCase, type) True type(unittest.TestCase) <type 'type'>
-- Scott Dial scott@scottdial.com scodial@cs.indiana.edu
Michael Foord wrote:
Benjamin Peterson wrote:
On Mon, Jul 14, 2008 at 6:18 PM, Ben Finney <ben+python@benfinney.id.au> wrote:
"Benjamin Peterson" <musiccomposition@gmail.com> writes:
On Mon, Jul 14, 2008 at 8:25 AM, Ben Finney <ben+python@benfinney.id.au> wrote:
Use new-style classes throughout --------------------------------
The following classes will inherit explicitly from the built-in `object` type, to make all classes in the module part of the new-style type hierarchy.
* ``TestResult`` * ``TestCase`` * ``TestSuite`` * ``TestLoader`` * ``_WritelnDecorator`` * ``TextTestRunner`` * ``TestProgram``
They already do. __metaclass__ = type is found in unittest.py.
Not in the copy I have. Is that in 3.x only, or in 2.x also?
Python 2.5.2 (r252:60911, Feb 22 2008, 07:57:53) [GCC 4.0.1 (Apple Computer, Inc. build 5363)] on darwin Type "help", "copyright", "credits" or "license" for more information.
import unittest isinstance(unittest.TestCase, object)
True
That proves nothing:
Python 2.5.2 (r252:60911, Feb 22 2008, 07:57:53) [GCC 4.0.1 (Apple Computer, Inc. build 5363)] on darwin Type "help", "copyright", "credits" or "license" for more information.
class x: pass ... isinstance(x, object) True isinstance(x, type) False type(x) <type 'classobj'>
Shouldn't isinstance(x, object) be True for any x in recent CPythons (and hopefully the other implementations too)? It's certainly so for functions and modules, for , as well as the less esoteric types and instances of classic classes. Something that often ties students' heads in knots (and this isn't from the introductory course):
isinstance(type, object) True isinstance(object, type) True
This is a classic property of general object hierarchies based on metaclasses. I remember teaching the SmallTalk-80 equivalent to M.Sc. studnets in the early 1980s, though the details are now lost in the mists of time. Eyes would always widen, and not everyone would get it. regards Steve -- Steve Holden +1 571 484 6266 +1 800 494 3119 Holden Web LLC http://www.holdenweb.com/
Benjamin Peterson wrote:
On Mon, Jul 14, 2008 at 6:18 PM, Ben Finney <ben+python@benfinney.id.au> wrote:
"Benjamin Peterson" <musiccomposition@gmail.com> writes:
On Mon, Jul 14, 2008 at 8:25 AM, Ben Finney <ben+python@benfinney.id.au> wrote:
Use new-style classes throughout --------------------------------
The following classes will inherit explicitly from the built-in `object` type, to make all classes in the module part of the new-style type hierarchy.
* ``TestResult`` * ``TestCase`` * ``TestSuite`` * ``TestLoader`` * ``_WritelnDecorator`` * ``TextTestRunner`` * ``TestProgram`` They already do. __metaclass__ = type is found in unittest.py. Not in the copy I have. Is that in 3.x only, or in 2.x also?
Python 2.5.2 (r252:60911, Feb 22 2008, 07:57:53) [GCC 4.0.1 (Apple Computer, Inc. build 5363)] on darwin Type "help", "copyright", "credits" or "license" for more information.
import unittest isinstance(unittest.TestCase, object) True
*Everything* is an instance of object. The question is whether the test classes are subclasses of object. In particular, issubclass(unittest.TestCase, object)? Either direct inheritance from object or '__metaclass__ = type' will make them so. With neither, they will not be. tjr
Ben Finney wrote:
[snip..]
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``
Although you may prefer the 'failIf' and 'failUnless' names, the consensus in the *last* discussion was that the 'assert*' names were to be preferred. I protest the removal of the assert names - and in the absence of likely consensus (and barring a dictat of course) I suggest this part of the proposal be excised. Michael -- http://www.ironpythoninaction.com/ http://www.voidspace.org.uk/ http://www.trypython.org/ http://www.ironpython.info/ http://www.theotherdelia.co.uk/ http://www.resolverhacks.net/
Ben Finney <ben+python <at> benfinney.id.au> writes:
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.
Just for information, here is the current distribution of the two synonym kinds: (on py3k) $ grep "self.assert" Lib/test/test_*.py | wc -l 14972 $ grep "self.fail" Lib/test/test_*.py | wc -l 1807 If no rational argument prevails, at least we have data on the past and current habits of the python-dev community. regards Antoine.
Significant updates are to the preamble (Python-Version field), the sections "Use new-style classes throughout", "Module attributes", and a new Rationale section "Removal of ``assert*`` names". :PEP: XXX :Title: Consolidating names and classes in the `unittest` module :Version: 0.0 :Last-Modified: 2008-07-15 :Author: Ben Finney <ben+python@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 and classes that constitute the API of the standard library `unittest` module, with the goal of removing redundant names, conforming with PEP 8, and eliminating classic classes. 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 use new-style classes, preventing e.g. straightforward use of ``super`` for calling the fixture set-up and tear-down methods. * It does not conform to 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 (specifically, that "there should be one, and preferably only one, obvious way to do it"). Specification ============= Use new-style classes throughout -------------------------------- The following classes are currently implemented as classic ("old-style") classes, with no metaclass. * ``TestResult`` * ``TestCase`` * ``TestSuite`` * ``TestLoader`` * ``_WritelnDecorator`` * ``TextTestRunner`` * ``TestProgram`` The `unittest` module will gain the following attribute, to set the default metaclass for classes in the module and thus make all classes in the module part of the new-style type hierarchy:: __metaclass__ = type 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. 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 ========= New-style classes ----------------- As a standard library module, `unittest` should not define any classic classes. 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 ("there should be one, and preferably only one, obvious way to do it"). Removal of ``assert*`` names ---------------------------- There is no overwhelming consensus on whether to remove the ``assert*`` names or the ``fail*`` names; both are in common use. This proposal argues the ``fail*`` names are slightly superior, for the following reasons: * 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. 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. Backwards Compatibility ======================= The names to be obsoleted should be deprecated and removed according to the schedule for modules in 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. .. Local Variables: mode: rst coding: utf-8 End: vim: filetype=rst :
On Mon, Jul 14, 2008 at 6:42 PM, Ben Finney <ben+python@benfinney.id.au> wrote:
Specification =============
Use new-style classes throughout --------------------------------
The following classes are currently implemented as classic ("old-style") classes, with no metaclass.
* ``TestResult`` * ``TestCase`` * ``TestSuite`` * ``TestLoader`` * ``_WritelnDecorator`` * ``TextTestRunner`` * ``TestProgram``
The `unittest` module will gain the following attribute, to set the default metaclass for classes in the module and thus make all classes in the module part of the new-style type hierarchy::
__metaclass__ = type
It's already done. Line 94-95 in unittest.py (trunk): # All classes defined herein are 'new-style' classes, allowing use of 'super()' __metaclass__ = type -- Cheers, Benjamin Peterson "There's no place like 127.0.0.1."
"Benjamin Peterson" <musiccomposition@gmail.com> writes:
On Mon, Jul 14, 2008 at 6:42 PM, Ben Finney <ben+python@benfinney.id.au> wrote:
The `unittest` module will gain the following attribute, to set the default metaclass for classes in the module and thus make all classes in the module part of the new-style type hierarchy::
__metaclass__ = type
It's already done.
Line 94-95 in unittest.py (trunk): # All classes defined herein are 'new-style' classes, allowing use of 'super()' __metaclass__ = type
Hmm, you're right; I see that in Python 2.5.2 'unittest.py'. Why is it not there in 3.0's 'unittest.py'? Is this achieved some other way? -- \ “Pinky, are you pondering what I'm pondering?” “I think so, | `\ Brain, but if they called them ‘Sad Meals’, kids wouldn't buy | _o__) them!” —_Pinky and The Brain_ | Ben Finney
Ben Finney wrote:
"Benjamin Peterson" <musiccomposition@gmail.com> writes:
On Mon, Jul 14, 2008 at 6:42 PM, Ben Finney <ben+python@benfinney.id.au> wrote:
The `unittest` module will gain the following attribute, to set the default metaclass for classes in the module and thus make all classes in the module part of the new-style type hierarchy::
__metaclass__ = type It's already done.
Line 94-95 in unittest.py (trunk): # All classes defined herein are 'new-style' classes, allowing use of 'super()' __metaclass__ = type
Hmm, you're right; I see that in Python 2.5.2 'unittest.py'.
Why is it not there in 3.0's 'unittest.py'? Is this achieved some other way?
In 3.0 there are only new-style classes, so nothing needs to be done there.
Eric Smith <eric+python-dev@trueblade.com> writes:
Ben Finney wrote:
"Benjamin Peterson" <musiccomposition@gmail.com> writes:
Line 94-95 in unittest.py (trunk): # All classes defined herein are 'new-style' classes, allowing use of 'super()' __metaclass__ = type
Hmm, you're right; I see that in Python 2.5.2 'unittest.py'.
Why is it not there in 3.0's 'unittest.py'? Is this achieved some other way?
In 3.0 there are only new-style classes, so nothing needs to be done there.
What makes that happen in the case where a class declares no superclass? Is there an invisible enforced "__metaclass__ = type" for every module? Where can I read about this change? -- \ “The apparent lesson of the Inquisition is that insistence on | `\ uniformity of belief is fatal to intellectual, moral, and | _o__) spiritual health.” —_The Uses Of The Past_, Herbert J. Muller | Ben Finney
Ben Finney wrote:
Eric Smith <eric+python-dev@trueblade.com> writes:
"Benjamin Peterson" <musiccomposition@gmail.com> writes:
Line 94-95 in unittest.py (trunk): # All classes defined herein are 'new-style' classes, allowing use of 'super()' __metaclass__ = type Hmm, you're right; I see that in Python 2.5.2 'unittest.py'.
Why is it not there in 3.0's 'unittest.py'? Is this achieved some other way? In 3.0 there are only new-style classes, so nothing needs to be done
Ben Finney wrote: there.
What makes that happen in the case where a class declares no superclass? Is there an invisible enforced "__metaclass__ = type" for every module? Where can I read about this change?
The magic is actually in 2.x, not in 3.0. In 2.x, if you don't explicit set the metaclass (or inherit explicitly from an object which sets it), then the default metaclass is 'classobj'. In 3.0, that magic goes away and the default metaclass is just 'type'. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
Nick Coghlan <ncoghlan@gmail.com> writes:
Ben Finney wrote:
What makes that happen in the case where a class declares no superclass? Is there an invisible enforced "__metaclass__ = type" for every module? Where can I read about this change?
The magic is actually in 2.x, not in 3.0. In 2.x, if you don't explicit set the metaclass (or inherit explicitly from an object which sets it), then the default metaclass is 'classobj'. In 3.0, that magic goes away and the default metaclass is just 'type'.
That helps. Thanks. -- \ “When I get real bored, I like to drive downtown and get a | `\ great parking spot, then sit in my car and count how many | _o__) people ask me if I'm leaving.” —Steven Wright | Ben Finney
On Tue, Jul 15, 2008 at 5:04 AM, Ben Finney <ben+python@benfinney.id.au> wrote:
"Benjamin Peterson" <musiccomposition@gmail.com> writes:
Line 94-95 in unittest.py (trunk): # All classes defined herein are 'new-style' classes, allowing use of 'super()' __metaclass__ = type
Hmm, you're right; I see that in Python 2.5.2 'unittest.py'.
Why is it not there in 3.0's 'unittest.py'? Is this achieved some other way?
All classes are new-style classes in 3.0! -- Cheers, Benjamin Peterson "There's no place like 127.0.0.1."
``set_up(…)`` Replaces ``setUp(…)`` . . ``tear_down(…)`` Replaces ``tearDown(…)``
Am I the only one who finds this sort of excessive pep-8 underscoring to be horrorific? Nobody I know spells setup and teardown as two words. I dread using the module with these new names. Underscores are not fun to type. They create a weird_mental_pause when reading them. It's not going to be easy to remember where they are used (ie. isinstance is still isinstance but isset is now is_set). Go figure.
fail_if_almost_equal
Another thought is that test suite code is going to get seriously crunched when the new, longer method names meet the 78/80 column pep 8 restrictions. class TestMisc(unittest.test_case): def lost_four_spaces_here_already(self): self.fail_if_almost_equal('were already on the 34th column before' 'writing anything substantive', self.testedobject.tested_method(arg1, arg2 + arg3, arg4) # Imagine typing and line wrapping dozens more tests like this Are there any ideas for some short, pithy, mnemonic names that are self-explantory and not full of underscores; something that wouldn't suck to type hundreds of times in a good test module? IMO, the current names are already too long.
* Explicit is better than implicit:
Don't forgot the opposing forces: Beautiful is better than ugly. Readability counts. These are especially important for the unittest module. When I'm making tests, I have to type self.very_long_method_name so many times it isn't funny. It's easy to just stop writing tests when you get tired of it. Long api names with underscores are a disincentive (IMO).
Use new-style classes throughout
This makes sense for 3.1 but of course we already get that automatically for 3.0 ;-) For 2.7, it doesn't make as much sense. Since 2.2 came out, there have been many discussions on changing standard library code to use new-style classes. There was always some concern about subtle changes in semantics and for the most part these requests were denied. I think this reason applies with even more force to the unittest module. Any risk that we subtlely break someone's test-suite is a small disaster. The 2.6 and 2.7 unittests need to be absolutely stable if they are to serve as a transition tool (providing a baseline the 3.0/3.1 is expected to match). Also, are there any expected benefits from making this change in 2.7? What will the module do differently? It seems like a risky change for zero-benefit. Raymond
Raymond Hettinger wrote:
``set_up(…)`` Replaces ``setUp(…)`` . . ``tear_down(…)`` Replaces ``tearDown(…)``
Am I the only one who finds this sort of excessive pep-8 underscoring to be horrorific?
Nobody I know spells setup and teardown as two words. I dread using the module with these new names. Underscores are not fun to type. They create a weird_mental_pause when reading them.
It's not going to be easy to remember where they are used (ie. isinstance is still isinstance but isset is now is_set). Go figure.
+1 setUp and tearDown should become setup and teardown.
fail_if_almost_equal
Another thought is that test suite code is going to get seriously crunched when the new, longer method names meet the 78/80 column pep 8 restrictions.
Well... "assert_not_equal" is slightly shorter, "assert_notequal" slightly more so. Still one char longer than the original though.
class TestMisc(unittest.test_case): def lost_four_spaces_here_already(self): self.fail_if_almost_equal('were already on the 34th column before' 'writing anything substantive', self.testedobject.tested_method(arg1, arg2 + arg3, arg4) # Imagine typing and line wrapping dozens more tests like this
Are there any ideas for some short, pithy, mnemonic names that are self-explantory and not full of underscores; something that wouldn't suck to type hundreds of times in a good test module? IMO, the current names are already too long.
* Explicit is better than implicit:
Don't forgot the opposing forces:
Beautiful is better than ugly. Readability counts.
These are especially important for the unittest module. When I'm making tests, I have to type self.very_long_method_name so many times it isn't funny. It's easy to just stop writing tests when you get tired of it. Long api names with underscores are a disincentive (IMO).
Use new-style classes throughout
This makes sense for 3.1 but of course we already get that automatically for 3.0 ;-)
For 2.7, it doesn't make as much sense. Since 2.2 came out, there have been many discussions on changing standard library code to use new-style classes. There was always some concern about subtle changes in semantics and for the most part these requests were denied. I think this reason applies with even more force to the unittest module. Any risk that we subtlely break someone's test-suite is a small disaster. The 2.6 and 2.7 unittests need to be absolutely stable if they are to serve as a transition tool (providing a baseline the 3.0/3.1 is expected to match).
Also, are there any expected benefits from making this change in 2.7? What will the module do differently?
It would allow you to use properties in testcases. Not sure if there is a usecase for this though. It looks like Benjamin Peterson is right, in Python 2.5 TestCase already appears to be a new style class: Python 2.5.2 (r252:60911, Feb 22 2008, 07:57:53) [GCC 4.0.1 (Apple Computer, Inc. build 5363)] on darwin Type "help", "copyright", "credits" or "license" for more information.
import unittest type(unittest.TestCase) <type 'type'>
It seems like a risky change for zero-benefit.
Looks like that part of the PEP is unnecessary. Michael
Raymond _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/fuzzyman%40voidspace.org.u...
-- http://www.ironpythoninaction.com/ http://www.voidspace.org.uk/ http://www.trypython.org/ http://www.ironpython.info/ http://www.theotherdelia.co.uk/ http://www.resolverhacks.net/
It looks like Benjamin Peterson is right, in Python 2.5 TestCase already appears to be a new style class:
Yep. I stand corrected. It looks like that changed five years ago (rev 28064). Not sure how that slipped through but it doesn't seem to have caused any problems. Raymond
On Tue, Jul 15, 2008 at 12:06 PM, Raymond Hettinger <python@rcn.com> wrote:
``set_up(…)`` Replaces ``setUp(…)``
. .
``tear_down(…)`` Replaces ``tearDown(…)``
Am I the only one who finds this sort of excessive pep-8 underscoring to be horrorific?
Nobody I know spells setup and teardown as two words. I dread using the module with these new names.
Hi, My name's Jonathan, and I spell "set up" as "set up" and "tear down" as "tear down".
It's not going to be easy to remember where they are used (ie. isinstance is still isinstance but isset is now is_set). Go figure.
Yes, guessability via consistency is the important thing here.
fail_if_almost_equal
Another thought is that test suite code is going to get seriously crunched when the new, longer method names meet the 78/80 column pep 8 restrictions.
class TestMisc(unittest.test_case): def lost_four_spaces_here_already(self): self.fail_if_almost_equal('were already on the 34th column before' 'writing anything substantive', self.testedobject.tested_method(arg1, arg2 + arg3, arg4) # Imagine typing and line wrapping dozens more tests like this
Are there any ideas for some short, pithy, mnemonic names that are self-explantory and not full of underscores; something that wouldn't suck to type hundreds of times in a good test module? IMO, the current names are already too long.
Well, "assert_" is strictly shorter than "fail_unless_".
* Explicit is better than implicit:
Don't forgot the opposing forces:
Beautiful is better than ugly. Readability counts.
These are especially important for the unittest module. When I'm making tests, I have to type self.very_long_method_name so many times it isn't funny. It's easy to just stop writing tests when you get tired of it. Long api names with underscores are a disincentive (IMO).
I find underscores easier to read—I suspect this will vary from person to person. Typing isn't an issue since I use an auto-complete function in my editor. jml
Jonathan Lange wrote:
My name's Jonathan, and I spell "set up" as "set up" and "tear down" as "tear down".
In English, it depends on how they're being used. As nouns they're single words, as verbs they're two words. As function names, they could be read either way, so it comes down to readability. To my eyes there is no loss of readability when omitting the underscores, so simplicity argues for leaving them out. -- Greg
Raymond Hettinger wrote:
``set_up(…)`` Replaces ``setUp(…)`` . . ``tear_down(…)`` Replaces ``tearDown(…)``
Am I the only one who finds this sort of excessive pep-8 underscoring to be horrorific?
Nobody I know spells setup and teardown as two words. I dread using the module with these new names. Underscores are not fun to type. They create a weird_mental_pause when reading them.
+1 And Merriam-Webster agrees, http://www.merriam-webster.com/dictionary/setup http://www.merriam-webster.com/dictionary/teardown Janzert
Raymond Hettinger wrote:
``set_up(…)`` Replaces ``setUp(…)`` . . ``tear_down(…)`` Replaces ``tearDown(…)``
Am I the only one who finds this sort of excessive pep-8 underscoring to be horrorific?
Definitely not. I thin we are in danger of insisting on a foolish consistency. I'd far prefer readability (hence my preference for is_not over not_is). There's also the (possibly marginal) point that people used to Junit can actually understand Python unit tests right now. Surely there's something to be said for consistency across languages, especially when the idea was lifted from Java in the first place.
Nobody I know spells setup and teardown as two words. I dread using the module with these new names. Underscores are not fun to type. They create a weird_mental_pause when reading them.
It's not going to be easy to remember where they are used (ie. isinstance is still isinstance but isset is now is_set). Go figure.
fail_if_almost_equal
Another thought is that test suite code is going to get seriously crunched when the new, longer method names meet the 78/80 column pep 8 restrictions.
class TestMisc(unittest.test_case): def lost_four_spaces_here_already(self): self.fail_if_almost_equal('were already on the 34th column before' 'writing anything substantive', self.testedobject.tested_method(arg1, arg2 + arg3, arg4) # Imagine typing and line wrapping dozens more tests like this
And the way Thunderbird has wrapped your example makes it obvious that 72 is a more satisfactory limit, though I agree transmissibility via email shouldn't necessarily be a factor.
Are there any ideas for some short, pithy, mnemonic names that are self-explantory and not full of underscores; something that wouldn't suck to type hundreds of times in a good test module? IMO, the current names are already too long.
* Explicit is better than implicit:
Don't forgot the opposing forces:
Beautiful is better than ugly. Readability counts.
These are especially important for the unittest module. When I'm making tests, I have to type self.very_long_method_name so many times it isn't funny. It's easy to just stop writing tests when you get tired of it. Long api names with underscores are a disincentive (IMO).
Use new-style classes throughout
This makes sense for 3.1 but of course we already get that automatically for 3.0 ;-)
For 2.7, it doesn't make as much sense. Since 2.2 came out, there have been many discussions on changing standard library code to use new-style classes. There was always some concern about subtle changes in semantics and for the most part these requests were denied. I think this reason applies with even more force to the unittest module. Any risk that we subtlely break someone's test-suite is a small disaster. The 2.6 and 2.7 unittests need to be absolutely stable if they are to serve as a transition tool (providing a baseline the 3.0/3.1 is expected to match).
I'm not quite sure how often it has to be pointed out that since 2.5 all unittest classes *are* new-style classes. Benjamin has, I believe, already mentioned twice that unittest.py contains __metaclass__ = type before any class declarations. While this has no effect on pre-2.2 implementations, surely it means that we've been using new-style classes for some time now. Or am I missing some subtlety? 2.5.1 says:
isinstance(unittest.TestCase, type) True
Also, are there any expected benefits from making this change in 2.7? What will the module do differently?
It seems like a risky change for zero-benefit.
Better revert it, then :-) But easier to just drop that sentence from the PEP. regards Steve -- Steve Holden +1 571 484 6266 +1 800 494 3119 Holden Web LLC http://www.holdenweb.com/
Raymond Hettinger writes:
Nobody I know spells setup and teardown as two words.
I set up a house of cards. When I'm done, I'm done with setup. Similarly for "tear down" and "teardown". The two word forms are verbs, the one word forms are nouns. I don't think it's worth a column to make that distinction, though.
Another thought is that test suite code is going to get seriously crunched when the new, longer method names meet the 78/80 column pep 8 restrictions.
class TestMisc(unittest.test_case): def lost_four_spaces_here_already(self):
Eight spaces, actually. Make that 13 by the time you get to the "." after "self" in the next line.
Are there any ideas for some short, pithy, mnemonic names that are self-explantory and not full of underscores; something that wouldn't suck to type hundreds of times in a good test module?
"test" or "check" instead of "assert" or "fail_unless" comes to mind to shorten the prefix. But the best I can come up with for "fail_unless_equal" is something like "equalize" which really fails EIBTI.
"Raymond Hettinger" <python@rcn.com> writes:
``set_up(…)`` Replaces ``setUp(…)`` . . ``tear_down(…)`` Replaces ``tearDown(…)``
Am I the only one who finds this sort of excessive pep-8 underscoring to be horrorific?
Nobody I know spells setup and teardown as two words.
I spell them as two words. The existing unittest framework also spells them as two words, using camelCase.
It's not going to be easy to remember where they are used (ie. isinstance is still isinstance but isset is now is_set). Go figure.
I don't see the connection of this sentence to the existing discussion.
Another thought is that test suite code is going to get seriously crunched when the new, longer method names meet the 78/80 column pep 8 restrictions.
class TestMisc(unittest.test_case): def lost_four_spaces_here_already(self): self.fail_if_almost_equal('were already on the 34th column before' 'writing anything substantive', self.testedobject.tested_method(arg1, arg2 + arg3, arg4) # Imagine typing and line wrapping dozens more tests like this
Yikes. Why are you using such huge indents? Break the line where indents won't be so enormous. class TestMisc(unittest.test_case): def lost_four_spaces_here_already(self): self.fail_if_almost_equal( 'we know this is going to be long, so we indent before' 'writing anything substantive', self.testedobject.tested_method( arg1, arg2 + arg3, arg4)
Are there any ideas for some short, pithy, mnemonic names that are self-explantory and not full of underscores; something that wouldn't suck to type hundreds of times in a good test module? IMO, the current names are already too long.
I'm very much in favour of having the namessay exactly what they're testing. They need to be long because there are many of them doing slightly-different things, so need careful disambiguation.
Beautiful is better than ugly. Readability counts.
These are especially important for the unittest module. When I'm making tests, I have to type self.very_long_method_name so many times it isn't funny. It's easy to just stop writing tests when you get tired of it. Long api names with underscores are a disincentive (IMO).
Yes, this is something that deserves consideration. -- \ “Why was I with her? She reminds me of you. In fact, she | `\ reminds me more of you than you do!” —Groucho Marx | _o__) | Ben Finney
Ben Finney wrote:
self.fail_if_almost_equal('were already on the 34th column before' 'writing anything substantive', self.testedobject.tested_method(arg1, arg2 + arg3, arg4) # Imagine typing and line wrapping dozens more tests like this
Yikes. Why are you using such huge indents? Break the line where indents won't be so enormous.
While true, that doesn't change the fact that fail_if_almost_equal is an undesirably long method name.
I'm very much in favour of having the namessay exactly what they're testing. They need to be long because there are many of them doing slightly-different things, so need careful disambiguation.
The "almost_equal" methods for floating point comparison are the main culprits for excessive method length, closely followed by the "fail_unless" variants. (21, 'failUnlessAlmostEqual') -> 24 with underscores (21, 'assertNotAlmostEquals') -> 25 with underscores (20, 'assertNotAlmostEqual') -> 24 with underscores (18, 'assertAlmostEquals') -> 20 with underscores (17, 'failIfAlmostEqual') -> 20 with underscores (17, 'assertAlmostEqual') -> 19 with underscores (16, 'failUnlessRaises') -> 18 with underscores (15, 'failUnlessEqual') -> 17 with underscores (15, 'assertNotEquals') -> 17 with underscores (14, 'assertNotEqual') -> 16 with underscores (12, 'assertRaises') -> 13 with underscores (12, 'assertEquals') -> 13 with underscores (11, 'failIfEqual') -> 13 with underscores (11, 'assertFalse') -> 12 with underscores (11, 'assertEqual') -> 12 with underscores (10, 'failUnless') -> 11 with underscores (10, 'assertTrue') -> 11 with underscores (7, 'assert_') -> 7 with underscores (6, 'failIf') -> 7 with underscores (4, 'fail') -> 4 with underscores One option for rationalising the API would be to merely keep the shortest version of each phrase (i.e. use assert* instead of fail_unless* for the positive tests and fail_if* instead of assert_not* for the negative tests, and always drop the trailing 's' from 'equals'). This simple rule would eliminate 12 of the 20 methods (including the four longest method names and 7 of the longest 9), leaving the following minimal set of methods: fail_if_almost_equal (20) assert_almost_equal (19) assert_raises (13) fail_if_equal (13) assert_equal (12) assert_ (7) fail_if (7) fail (4) Adding in possible positive and negative forms of the proposed new methods (using words more appropriate for a method rather than copying the infix operators): fail_if_identical (17) fail_if_contains (16) assert_identical (16) assert_contains (15) Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
Nick Coghlan <ncoghlan@gmail.com> writes:
fail_if_almost_equal is an undesirably long method name.
I disagree. It says what the method does, as precisely as necessary to distinguish it from other methods of the class. It is as long as it needs to be to say that while still being readable and PEP 8 compliant.
One option for rationalising the API would be to merely keep the shortest version of each phrase (i.e. use assert* instead of fail_unless* for the positive tests and fail_if* instead of assert_not* for the negative tests, and always drop the trailing 's' from 'equals').
I think clarity, consistency, and discoverability are more important criteria than saving a few characters in a method name.
Adding in possible positive and negative forms of the proposed new methods
A major point of this PEP is to *remove* redundant names in the API. -- \ “It's dangerous to be right when the government is wrong.” | `\ —Francois Marie Arouet Voltaire | _o__) | Ben Finney
Ben Finney wrote:
Nick Coghlan <ncoghlan@gmail.com> writes:
One option for rationalising the API would be to merely keep the shortest version of each phrase (i.e. use assert* instead of fail_unless* for the positive tests and fail_if* instead of assert_not* for the negative tests, and always drop the trailing 's' from 'equals').
I think clarity, consistency, and discoverability are more important criteria than saving a few characters in a method name.
Adding in possible positive and negative forms of the proposed new methods
A major point of this PEP is to *remove* redundant names in the API.
def it_is_a_truth_universally_recognised_that(...): def we_are_all_feckin_doomed_unless(...): should be quite sufficient ;-) entia-non-sunt-multiplicanda-ly y'rs G.
Nick Coghlan wrote:
One option for rationalising the API would be to merely keep the shortest version of each phrase (i.e. use assert* instead of fail_unless* for the positive tests and fail_if* instead of assert_not* for the negative tests, and always drop the trailing 's' from 'equals').
Disclaimer: I'm not convinced the ideas in this message are actually a good idea myself. But I found them intriguing enough to bother posting them. To give some idea of how the different styles would look, here's an example (based on one of the tests in test_runpy) using a combination of assert* and fail_if* names: self.fail_if_contains(d1, "result") self.assert_identical(d2["initial"], initial) self.assert_equal(d2["result"], self.expected_result) self.assert_equal(d2["nested"]["x"], 1) self.assert_(d2["run_name_in_sys_modules"]) self.assert_(d2["module_in_sys_modules"]) self.assert_identical(d2["run_argv0"], file) self.assert_identical(sys.argv[0], saved_argv0) self.fail_if_contains(sys.modules, name) A somewhat odd thought that occurred to me is that the shortest possible way of writing negative assertions (i.e. asserting that something is not the case) is to treat them as denials and use the single word 'deny'. This approach would give: Test assertions: assert_almost_equal assert_identical assert_contains assert_raises assert_equal assert_ Test denials (negative assertions): deny_almost_equal (17) deny_identical (14) deny_contains (13) deny_equal (10) deny (4) Explicit failure: fail (4) Using the test_runpy example assertions: self.deny_contains(d1, "result") self.assert_identical(d2["initial"], initial) self.assert_equal(d2["result"], self.expected_result) self.assert_equal(d2["nested"]["x"], 1) self.assert_(d2["run_name_in_sys_modules"]) self.assert_(d2["module_in_sys_modules"]) self.assert_identical(d2["run_argv0"], file) self.assert_identical(sys.argv[0], saved_argv0) self.deny_contains(sys.modules, name) I actually quite like that - and it saves not only several characters, but also an underscore over the fail_if* and assert_not* variants. The second odd thought was what happens if the 'assert' is made implicit? Test assertions: are_almost_equal are_identical does_contain does_raise are_equal assert_ Test negative assertions: not_almost_equal not_identical not_contains not_equal not_ Explicit failure: fail Using the test_runpy example assertions: self.not_contains(d1, "result") self.are_identical(d2["initial"], initial) self.are_equal(d2["result"], self.expected_result) self.are_equal(d2["nested"]["x"], 1) self.assert_(d2["run_name_in_sys_modules"]) self.assert_(d2["module_in_sys_modules"]) self.are_identical(d2["run_argv0"], file) self.are_identical(sys.argv[0], saved_argv0) self.not_contains(sys.modules, name) Yecch, I think that idea can be safely consigned to the mental trash heap. Another late night API concept: create a "check" object with the LHS of the binary operation being asserted, then use methods on that check object to supply the RHS: self.check("result").not_in(d1) self.check(d2["initial"]).is_(initial) self.check(d2["result"]).equals(self.expected_result) self.check(d2["nested"]["x"]).equals(1) self.assert_(d2["run_name_in_sys_modules"]) self.assert_(d2["module_in_sys_modules"]) self.check(d2["run_argv0"]).is_(file) self.check(sys.argv[0]).is_(saved_argv0) self.check(name).not_in(sys.modules) This approach would also be handy if you needed to check multiple conditions on a single result: check = self.check(result) check.is_equal(expected_result) # Right answer check.is_not(expected_result) # Fresh object check.is_(recent_results[-1]) # Recorded properly Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
Nick Coghlan <ncoghlan@gmail.com> writes:
A somewhat odd thought that occurred to me is that the shortest possible way of writing negative assertions (i.e. asserting that something is not the case) is to treat them as denials and use the single word 'deny'.
-1 This, to me, is neither intuitive nor meaningful in context. The term "deny" is strongly linked to its antonym, "permit". Whom is being denied? What have they asked to do that I am denying in my test? I think in terms of "true or false", or "pass or fail". I'm making statements about behaviour of the program, not about permitting or denying something. -- \ “The industrial system is profoundly dependent on commercial | `\ television and could not exist in its present form without it.” | _o__) —John Kenneth Galbraith, _The New Industrial State_, 1967 | Ben Finney
Ben Finney wrote:
Nick Coghlan <ncoghlan@gmail.com> writes:
the shortest possible way of writing negative assertions (i.e. asserting that something is not the case) is to treat them as denials and use the single word 'deny'.
This, to me, is neither intuitive nor meaningful in context. The term "deny" is strongly linked to its antonym, "permit".
"Deny" also has the meaning of claiming that something is not true (as in "deny an allegation"). When used that way, it's not an antonym of "permit". However, that meaning doesn't quite seem to fit here, as we don't just want to claim that the condition is false, but *ensure* that it's false. I can't think of a single word offhand that means that. -- Greg
Greg Ewing wrote:
Ben Finney wrote:
Nick Coghlan <ncoghlan@gmail.com> writes:
the shortest possible way of writing negative assertions (i.e. asserting that something is not the case) is to treat them as denials and use the single word 'deny'.
This, to me, is neither intuitive nor meaningful in context. The term "deny" is strongly linked to its antonym, "permit".
"Deny" also has the meaning of claiming that something is not true (as in "deny an allegation"). When used that way, it's not an antonym of "permit".
However, that meaning doesn't quite seem to fit here, as we don't just want to claim that the condition is false, but *ensure* that it's false. I can't think of a single word offhand that means that.
That was the meaning I was going for, but I agree that it doesn't quite fit well enough to make it a good idea. There's a reason I put that disclaimer at the top of the message :) What did you think of the "check" idea at the end of the email? Test assertions: check(x).almost_equal(y) check(x).is_(y) check(x).in_(y) check(x).equals(y) Test negative assertions: check(x).not_almost_equal(y) check(x).is_not(y) check(x).not_in(y) check(x).not_equal(y) Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
Nick Coghlan <ncoghlan@gmail.com> writes:
What did you think of the "check" idea at the end of the email?
Test assertions: check(x).almost_equal(y) check(x).is_(y) check(x).in_(y) check(x).equals(y)
Test negative assertions: check(x).not_almost_equal(y) check(x).is_not(y) check(x).not_in(y) check(x).not_equal(y)
-1 'check' is even less explicit about what will happen than 'assert'. At least the latter has existing programming-language connotations of "fail immediately if not true", which 'check' lacks. -- \ “I used to work in a fire hydrant factory. You couldn't park | `\ anywhere near the place.” —Steven Wright | _o__) | Ben Finney
Nick Coghlan wrote: [...]
What did you think of the "check" idea at the end of the email?
Test assertions: check(x).almost_equal(y) check(x).is_(y) check(x).in_(y) check(x).equals(y)
Test negative assertions: check(x).not_almost_equal(y) check(x).is_not(y) check(x).not_in(y) check(x).not_equal(y)
Wow. This is a textbook example of a bikeshed discussion. The names (and now syntax!) of assertions are the most cosmetic issue there is with the unittest module, yet I see over *200* messages about it. Deeper, but important issues, such as how to raise structured exceptions that a GUI test runner could usefully display and introspect, are ignored. I can list lots of others. For assertions, just flip a coin already (or a roll a d20, perhaps...). Can we perhaps devote this considerable energy to significant improvements, please? -Andrew.
Andrew Bennetts <andrew-pythondev@puzzling.org> writes:
This is a textbook example of a bikeshed discussion. The names (and now syntax!) of assertions are the most cosmetic issue there is with the unittest module, yet I see over *200* messages about it.
I've found it much more insightful than you seem to. I'm sorry it hasn't been of more interest to you, but please don't discount the value of this discussion for the rest of us.
Deeper, but important issues, such as how to raise structured exceptions that a GUI test runner could usefully display and introspect, are ignored. I can list lots of others.
I look forward to you writing, promoting, and championing the PEP document(s) for these issues that you consider important. -- \ “Some forms of reality are so horrible we refuse to face them, | `\ unless we are trapped into it by comedy. To label any subject | _o__) unsuitable for comedy is to admit defeat.” —Peter Sellers | Ben Finney
On 15/07/2008, Raymond Hettinger <python@rcn.com> wrote:
``set_up(…)`` Replaces ``setUp(…)``
. .
``tear_down(…)`` Replaces ``tearDown(…)``
Am I the only one who finds this sort of excessive pep-8 underscoring to be horrorific?
No.
Nobody I know spells setup and teardown as two words. I dread using the module with these new names. Underscores are not fun to type. They create a weird_mental_pause when reading them.
I agree. The java-esque setUp and tearDown are (in my view) ugly, but set_up and tear_down are as bad. +1 for setup and teardown. +1 also for thinking a *lot* harder about naming, and not just going with phrases_joined_up_with_underscores, or phrasesWithNoSpacesAndFunnyCapitalisation. There are certainly issues with the simple assert expression approach, but ugliness and unreadability aren't two of them. Paul.
Ben Finney writes:
Removal of ``assert*`` names ----------------------------
There is no overwhelming consensus on whether to remove the ``assert*`` names or the ``fail*`` names;
7 to 1 is overwhelming in my book. See Message-ID: <loom.20080714T230912-310@post.gmane.org> From: Antoine Pitrou <solipsis@pitrou.net> While people's preferences are important, I think there is a very good case to made that keeping this much continuity in the test suite as possible is more so.
* 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.
EIBTI applies with the most force to "local" names, ie, specific to a particular function, class, or program. Here we propose to impose a community-wide convention. I think we can document it explicitly and expect near-instant uptake on the appropriate connotations to "assert" (especially since that connotation is pretty much universal across languages with an assert facility, anyway).
* Avoid false implication: The test methods do not have any necessary connection with the built-in ``assert`` statement.
Data point: Use of `Assert' as a test method in the XEmacs test suite has never caused any confusion with either C-level asserts, or with the Lisp function `assert'.
"Stephen J. Turnbull" <stephen@xemacs.org> writes:
Ben Finney writes:
Removal of ``assert*`` names ----------------------------
There is no overwhelming consensus on whether to remove the ``assert*`` names or the ``fail*`` names;
7 to 1 is overwhelming in my book. See Message-ID: <loom.20080714T230912-310@post.gmane.org> From: Antoine Pitrou <solipsis@pitrou.net>
That measured only usage of unittest *within the Python standard library*. Is that the only body of unittest-using code we need consider?
While people's preferences are important, I think there is a very good case to made that keeping this much continuity in the test suite as possible is more so.
That's a separate argument, then. One which I don't dismiss, but it needs to be stated as such. -- \ “A poet more than thirty years old is simply an overgrown | `\ child.” —Henry L. Mencken | _o__) | Ben Finney
-On [20080715 12:35], Ben Finney (bignose+hates-spam@benfinney.id.au) wrote:
That measured only usage of unittest *within the Python standard library*. Is that the only body of unittest-using code we need consider?
Some greps on random Python projects give me a 4-10:1 ratio for assert* versus fail*. Personally I also find the assert* syntax preferable over fail*. -- Jeroen Ruigrok van der Werven <asmodai(-at-)in-nomine.org> / asmodai イェルーン ラウフロック ヴァン デル ウェルヴェン http://www.in-nomine.org/ | http://www.rangaku.org/ | GPG: 2EAC625B If I am telling you the Truth now, do you believe it..?
On Tue, 15 Jul 2008 08:54:25 pm Jeroen Ruigrok van der Werven wrote:
Some greps on random Python projects give me a 4-10:1 ratio for assert* versus fail*.
Without knowing what those "random" projects are, or what the grep was, it's impossible to interpret that statistic. For example, is it biased by the existence of the assert keyword? Do they represent programmers' free choices, or the projects' compulsory style guides?
Personally I also find the assert* syntax preferable over fail*.
Thank you for acknowledging that as a personal preference rather than making another spurious claim of objective superiority. -- Steven
Ben Finney writes:
Message-ID: <loom.20080714T230912-310@post.gmane.org> From: Antoine Pitrou <solipsis@pitrou.net>
That measured only usage of unittest *within the Python standard library*. Is that the only body of unittest-using code we need consider?
Yes, for the purposes of this PEP. We already know that many people want various different things. You want fail* /rather than/ assert*, but Steven d'Aprono wants /both/, and I prefer assert* /exclusively/. I don't see why we all shouldn't be satisfied[1], so the content of unittest should not set policy for our own projects. So there should be other modules (perhaps in the stdlib, perhaps not) to satisfy those preferences not catered to by stdlib's unittest. Thus this PEP should restrict it's concern to revising unittest to conform to PEPs and help standardize Python's own testing, without trying to impose standards on the whole community of Python users. That's my rationale. YMMV. Footnotes: [1] For myself, if the fail*-only proposal wins, I'd switch to that; fighting the tide on this isn't my idea of fun. But other assert* fans may be more faithful to their original preference.
Stephen J. Turnbull wrote:
Yes, for the purposes of this PEP. We already know that many people want various different things. You want fail* /rather than/ assert*, but Steven d'Aprono wants /both/, and I prefer assert* /exclusively/. I don't see why we all shouldn't be satisfied[1], so the content of unittest should not set policy for our own projects. So there should be other modules (perhaps in the stdlib, perhaps not) to satisfy those preferences not catered to by stdlib's unittest.
It should be trivial to write a module 'unitfail' that would 'from unittest import *' and then flip the assert names to fail names. The only question is whether it needs to be in the stdlib or merely PyPI. I word this this way because Guido has already blessed the assert forms for unittest. If he is persuaded otherwise, revise accordingly.
Thus this PEP should restrict it's concern to revising unittest to conform to PEPs and help standardize Python's own testing, without trying to impose standards on the whole community of Python users.
For the community as a whole, all stdlib modules are suggestions and examples, not commands. tjr
Terry Reedy writes:
For the community as a whole, all stdlib modules are suggestions and examples, not commands.
Well, even if "standard" is too strong a word, the DeprecationWarnings and eventual removal of the methods surely constitute an imposition.
"Stephen J. Turnbull" <stephen@xemacs.org> writes:
Terry Reedy writes:
For the community as a whole, all stdlib modules are suggestions and examples, not commands.
Well, even if "standard" is too strong a word, the DeprecationWarnings and eventual removal of the methods surely constitute an imposition.
I understood Terry's statement as meaning that there is no commandment to use the standard library 'unittest' module at all. If a user doesn't like the behaviour of a module (such as 'unittest') in the standard library, they can accept the burden of implementing one that behaves the way they like. -- \ “I never forget a face, but in your case I'll be glad to make | `\ an exception.” —Groucho Marx | _o__) | Ben Finney
Ben Finney wrote:
"Stephen J. Turnbull" <stephen@xemacs.org> writes:
Terry Reedy writes:
For the community as a whole, all stdlib modules are suggestions and examples, not commands.
Well, even if "standard" is too strong a word, the DeprecationWarnings and eventual removal of the methods surely constitute an imposition.
I understood Terry's statement as meaning that there is no commandment to use the standard library 'unittest' module at all. If a user doesn't like the behaviour of a module (such as 'unittest') in the standard library, they can accept the burden of implementing one that behaves the way they like.
I have written the few specialized test functions that I need my current project. I did this after considering (and taking ideas from) py.test. I will also use doctest as a supplement. I also meant that one who does use something is free to rename it. I commonly import xskjfl as x;-)
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Ben Finney wrote:
"Stephen J. Turnbull" <stephen@xemacs.org> writes:
Terry Reedy writes:
For the community as a whole, all stdlib modules are suggestions and examples, not commands.
Well, even if "standard" is too strong a word, the DeprecationWarnings and eventual removal of the methods surely constitute an imposition.
I understood Terry's statement as meaning that there is no commandment to use the standard library 'unittest' module at all. If a user doesn't like the behaviour of a module (such as 'unittest') in the standard library, they can accept the burden of implementing one that behaves the way they like.
One might argue that making gratutiously backward-incompatible changes to the existing 'unittest' module should fall under that heading: stability in that package is going to be crucial for projects which have any hope of making it across the 2-to-3 gulf, and they won't all get there at once. If camelCase / duplicated names are such a pain, write a *new* module, 'unittest2', and port Python's tests to use it, thereby leaving the much larger volume of not-in-Python tests still working. One might then remove the 'unittest' module in a later release, packaging it as a standalone distibution for the projects which still need it. Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tseaver@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFIfk2/+gerLs4ltQ4RAjd7AJ4iRGnOp+Udw9Jth/VtdMJhJWL50QCePUEY O1fn7KSSIfIGr0HbIX2xl74= =s0m/ -----END PGP SIGNATURE-----
Tres Seaver wrote:
-----BEGIN PGP SIGNED MESSAGE-----
If camelCase / duplicated names are such a pain, write a *new* module, 'unittest2', and port Python's tests to use it, thereby leaving the much larger volume of not-in-Python tests still working. One might then remove the 'unittest' module in a later release, packaging it as a standalone distibution for the projects which still need it.
There was, at one time at least, some degree of consensus for dropping the Fail names and keeping the Assert names, which appear to comprise 75%-80% of usage 'in the wild'. There seems to less consensus on also changing the Assert names from CamelCase to under_score versions. So I was also thinking about a 'unittest2', recommended for new projects and gradual changeover of the Python test suite. 'Unittest' could be left unchanged but gradually disrecommended, deprecated, and removed (perhaps in 4.0 if not before).
Ben Finney wrote:
"Stephen J. Turnbull" <stephen@xemacs.org> writes:
Ben Finney writes:
Removal of ``assert*`` names ----------------------------
There is no overwhelming consensus on whether to remove the ``assert*`` names or the ``fail*`` names;
7 to 1 is overwhelming in my book. See Message-ID: <loom.20080714T230912-310@post.gmane.org> From: Antoine Pitrou <solipsis@pitrou.net>
That measured only usage of unittest *within the Python standard library*. Is that the only body of unittest-using code we need consider?
Three more data points then: bzr: 13228 assert* vs. 770 fail*. Twisted: 6149 assert* vs. 1666 fail*. paramiko: 431 assert* vs. 4 fail*. The data seems pretty overwhelmingly in favour of keeping assert*. -Andrew.
Andrew Bennetts <andrew-pythondev@puzzling.org> writes:
Ben Finney wrote:
"Stephen J. Turnbull" <stephen@xemacs.org> writes:
Message-ID: <loom.20080714T230912-310@post.gmane.org> From: Antoine Pitrou <solipsis@pitrou.net>
That measured only usage of unittest *within the Python standard library*. Is that the only body of unittest-using code we need consider?
Three more data points then:
bzr: 13228 assert* vs. 770 fail*. Twisted: 6149 assert* vs. 1666 fail*. paramiko: 431 assert* vs. 4 fail*.
The data seems pretty overwhelmingly in favour of keeping assert*.
Noted, thanks. So far I have "precedent and tradition" and "positive admonition looks better" in support of preferring the 'assert*' names. Are there any others? I believe I've stated (in the most-recent PEP revision) the strongest reasons in favour of the 'fail*' names. This all gets summarised in the Rationale section for the PEP. -- \ “Killing the creator was the traditional method of patent | `\ protection” —Terry Pratchett, _Small Gods_ | _o__) | Ben Finney
On Tue, Jul 15, 2008 at 2:48 PM, Ben Finney <ben+python@benfinney.id.au> wrote:
So far I have "precedent and tradition" and "positive admonition looks better" in support of preferring the 'assert*' names. Are there any others?
I've been told by a couple of non-programmers that "failUnless" is more intuitive than "assert" if only for the reason that its unclear what "assert" might do. This is similar to one of the arguments raised in the PEP, but considered from the point of view of someone new to test frameworks it could be all the more important. On another note, while I understand that consistency is a good thing is it really that important in test suites? Obviously the unittest module itself should be internally consistent but why not provide people using the all the synonyms they might want? I can see people just wrapping TestCase to add the synonyms back in. Richard
On Tue, Jul 15, 2008 at 03:00:30PM +0100, Richard Thomas wrote: -> On Tue, Jul 15, 2008 at 2:48 PM, Ben Finney <ben+python@benfinney.id.au> wrote: -> > So far I have "precedent and tradition" and "positive admonition looks -> > better" in support of preferring the 'assert*' names. Are there any -> > others? -> -> I've been told by a couple of non-programmers that "failUnless" is -> more intuitive than "assert" if only for the reason that its unclear -> what "assert" might do. This is similar to one of the arguments raised -> in the PEP, but considered from the point of view of someone new to -> test frameworks it could be all the more important. Maybe this is an unnecessarily hard line, but if someone doesn't know what "assert" does, then they should go look up the keyword -- it's part of Python (and present in C, C++, Perl, and PHP as well). So unless the person is new to Python AND testing, they should know it. -> On another note, while I understand that consistency is a good thing -> is it really that important in test suites? Obviously the unittest -> module itself should be internally consistent but why not provide -> people using the all the synonyms they might want? I can see people -> just wrapping TestCase to add the synonyms back in. While I don't have a strong position on the assert* vs fail*, I think consistency and simplicity in test suites is at least as important as in code: if you have to work hard to understand and debug your test suites, you've done something seriously wrong in building your tests. Tests should be as simple as possible, and no simpler. --titus -- C. Titus Brown, ctb@msu.edu
"C. Titus Brown" <ctb@msu.edu> writes:
On Tue, Jul 15, 2008 at 03:00:30PM +0100, Richard Thomas wrote: -> I've been told by a couple of non-programmers that "failUnless" -> is more intuitive than "assert" if only for the reason that its -> unclear what "assert" might do. This is similar to one of the -> arguments raised in the PEP, but considered from the point of -> view of someone new to test frameworks it could be all the more -> important.
Maybe this is an unnecessarily hard line, but if someone doesn't know what "assert" does, then they should go look up the keyword -- it's part of Python (and present in C, C++, Perl, and PHP as well). So unless the person is new to Python AND testing, they should know it.
That's exactly the problem with the 'assert*' names: The test methods of TestCase *don't* do the same thing as the Python 'assert' statement, and aren't meant to. The association is confusing, even (especially?) if one knows what the 'assert' statement does.
While I don't have a strong position on the assert* vs fail*, I think consistency and simplicity in test suites is at least as important as in code: if you have to work hard to understand and debug your test suites, you've done something seriously wrong in building your tests.
Yes. While I prefer the 'fail*' names, I would prefer to lose *either* of 'assert*' or 'fail*' than to retain redundant names in the API. -- \ “If I had known what it would be like to have it all... I might | `\ have been willing to settle for less.” —Jane Wagner, via Lily | _o__) Tomlin | Ben Finney
On Tue, Jul 15, 2008 at 3:34 PM, Ben Finney <ben+python@benfinney.id.au> wrote:
That's exactly the problem with the 'assert*' names: The test methods of TestCase *don't* do the same thing as the Python 'assert' statement, and aren't meant to. The association is confusing, even (especially?) if one knows what the 'assert' statement does.
The parallel is intentional. They even raise the same exception. Too bad it doesn't work for you; get over it. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
Richard Thomas wrote:
I've been told by a couple of non-programmers that "failUnless" is more intuitive than "assert" if only for the reason that its unclear what "assert" might do.
But test frameworks are for use by programmers, not non-programmers. Given that it's a test framework, would a programmer really find it hard to guess what these mean? -- Greg
So far I have "precedent and tradition" and "positive admonition looks better" in support of preferring the 'assert*' names. Are there any others?
Avoiding double negatives. (and don't tell me "fail" is not a negative word, because you just used the phrase "positive admonition" to refer to the assert* variants, which implies quite clearly that the fail* variants are on the negative side ;-)) Regards Antoine.
Antoine Pitrou <solipsis@pitrou.net> writes:
(and don't tell me "fail" is not a negative word, because you just used the phrase "positive admonition" to refer to the assert* variants, which implies quite clearly that the fail* variants are on the negative side ;-))
This "fail is a negative word" has already been rebutted, by native speakers of English. If you don't want to hear "fail is not a negative word", I can't help you. This doesn't seem to introduce anything new, so I'll leave the existing summary of this argument as is in the PEP. -- \ “Holy unrefillable prescriptions, Batman!” —Robin | `\ | _o__) | Ben Finney
Ben Finney <ben+python <at> benfinney.id.au> writes:
This "fail is a negative word" has already been rebutted, by native speakers of English.
Well, Stephen's and Greg's own answers notwithstanding, if you really want an authoritative answer, the best would be to open a dictionary and contrast the given definitions... * http://www.thefreedictionary.com/fail "To prove deficient or lacking; perform ineffectively or inadequately; To be unsuccessful" * http://www.thefreedictionary.com/assert "To state or express positively" But perhaps there is enough of this fail/assert discussion now :-) Regards Antoine.
Antoine Pitrou <solipsis@pitrou.net> writes:
* http://www.thefreedictionary.com/fail "To prove deficient or lacking; perform ineffectively or inadequately; To be unsuccessful"
Yes. It's a verb, not a negative modifer. To use it with a negative like "not" is not creating a "double negative". -- \ “It's dangerous to be right when the government is wrong.” | `\ —Francois Marie Arouet Voltaire | _o__) | Ben Finney
Ben Finney <ben+python <at> benfinney.id.au> writes:
* http://www.thefreedictionary.com/fail "To prove deficient or lacking; perform ineffectively or inadequately; To be unsuccessful"
Yes. It's a verb, not a negative modifer. To use it with a negative like "not" is not creating a "double negative".
Ok, let's stop it there. I'm tired of trying to point the obvious.
Ben Finney writes:
This "fail is a negative word" has already been rebutted, by native speakers of English.
Not successfully, it hasn't. Steven d'Aprano describes one style of testing as "the test passes if it fails to fail in each of a sequence of cases." That is perfectly good English, which makes no sense if "fail" completely lacks the semantics of negation. The intuition that "fail" is a negative word is thus well-founded in standard usage. By the way, a native speaker is a person who has no need to understand how his language works; he just uses it. Being a native speaker doesn't qualify one as an authority on her language.
"Stephen J. Turnbull" <stephen@xemacs.org> writes:
The intuition that "fail" is a negative word is thus well-founded in standard usage.
That's not the same thing as "fail" being a negative word in the sense meant by "double negative". That is, "not fail" is not a double negative; nor is "fail if X is not inside Y" a double negative. The use of "fail" in those phrases is a action, a verb, not a "negative". So, this issue of avoiding "fail" in order to "avoid double negatives" has no basis in the use of "fail" in unittest. -- \ “It was half way to Rivendell when the drugs began to take | `\ hold” —Hunter S. Tolkien, _Fear and Loathing in Barad-Dûr_ | _o__) | Ben Finney
Ben Finney writes:
"Stephen J. Turnbull" <stephen@xemacs.org> writes:
The intuition that "fail" is a negative word is thus well-founded in standard usage.
That's not the same thing as "fail" being a negative word in the sense meant by "double negative".
So what? This whole exercise is about human psychology. If it were just a matter of defining things and we're done, it wouldn't matter how many identifiers we use or how they're spelled, right? You should treat those perceptions with respect when writing this kind of PEP, not deny them outright.
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Andrew Bennetts wrote:
Ben Finney wrote:
"Stephen J. Turnbull" <stephen@xemacs.org> writes:
Ben Finney writes:
Removal of ``assert*`` names ----------------------------
There is no overwhelming consensus on whether to remove the ``assert*`` names or the ``fail*`` names;
7 to 1 is overwhelming in my book. See Message-ID: <loom.20080714T230912-310@post.gmane.org> From: Antoine Pitrou <solipsis@pitrou.net> That measured only usage of unittest *within the Python standard library*. Is that the only body of unittest-using code we need consider?
Three more data points then:
bzr: 13228 assert* vs. 770 fail*.
Twisted: 6149 assert* vs. 1666 fail*.
paramiko: 431 assert* vs. 4 fail*.
The data seems pretty overwhelmingly in favour of keeping assert*.
FWIW: Zope2: 16878 'assert*', 2892 'fail*'. I would keep both by preference, rather than insist on a "foolish consistency." Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tseaver@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFIfOJI+gerLs4ltQ4RArw5AJ0fUzXiMefC4CQZDk4K0mlQFn3nAACfRLya CuhfeNQd8OUPFi5+E5aJN9M= =2Hsl -----END PGP SIGNATURE-----
Tres Seaver <tseaver@palladion.com> writes:
FWIW: Zope2: 16878 'assert*', 2892 'fail*'.
Thanks.
I would keep both by preference, rather than insist on a "foolish consistency."
The "consistency" argument leads to the PEP 8 names. The removal of redundant names is not made in the name of consistency, but of API simplicity. -- \ “Under democracy one party always devotes its chief energies to | `\ trying to prove that the other party is unfit to rule--and both | _o__) commonly succeed, and are right.” —Henry L. Mencken | Ben Finney
On Tue, Jul 15 2008 at 07:38:59PM BRT, Ben Finney <ben+python@benfinney.id.au> wrote:
Tres Seaver <tseaver@palladion.com> writes:
I would keep both by preference, rather than insist on a "foolish consistency."
+1
The "consistency" argument leads to the PEP 8 names. The removal of redundant names is not made in the name of consistency, but of API simplicity.
Isn't this *enourmous* discussion a clear sign that a lot of people *won't* find a trimmed-down API simpler? If assert* to fail* mapping is consistent, then voilá, simple, everyone can remember method names (+1 for PEP-8 renaming), everyone is free to think of each test as they please. rbp -- Rodrigo Bernardo Pimentel <rbp@isnomore.net> | GPG: <0x0DB14978>
Rodrigo Bernardo Pimentel <rbp@isnomore.net> writes:
On Tue, Jul 15 2008 at 07:38:59PM BRT, Ben Finney <ben+python@benfinney.id.au> wrote:
The "consistency" argument leads to the PEP 8 names. The removal of redundant names is not made in the name of consistency, but of API simplicity.
Isn't this *enourmous* discussion a clear sign that a lot of people *won't* find a trimmed-down API simpler?
The majority of this discussion hasn't been about whether or not to trim the API, so I'd have to answer no. Instead, it shows that people have different ideas of how it should be done.
If assert* to fail* mapping is consistent, then voilá, simple, everyone can remember method names (+1 for PEP-8 renaming), everyone is free to think of each test as they please.
Thanks for the feedback. -- \ “Pity the meek, for they shall inherit the earth.” —Donald | `\ Robert Perry Marquis | _o__) | Ben Finney
On Wed, Jul 16 2008 at 10:54:26AM BRT, Ben Finney <ben+python@benfinney.id.au> wrote:
Rodrigo Bernardo Pimentel <rbp@isnomore.net> writes:
On Tue, Jul 15 2008 at 07:38:59PM BRT, Ben Finney <ben+python@benfinney.id.au> wrote:
The "consistency" argument leads to the PEP 8 names. The removal of redundant names is not made in the name of consistency, but of API simplicity.
Isn't this *enourmous* discussion a clear sign that a lot of people *won't* find a trimmed-down API simpler?
The majority of this discussion hasn't been about whether or not to trim the API, so I'd have to answer no. Instead, it shows that people have different ideas of how it should be done.
Ok, I've oversimplified my sentence. In more words, what I meant was that I don't think the benefits of getting rid of assert* or fail* methods are very clear (thus my "+1" on Tres's suggestion of leaving both), and it doesn't seem to me like we're progressing in the direction of reaching an agreement (though there has been great exposition of arguments). I mean, the benefits of removing fail* seem clear for some people, but are strongly opposed by others, and even the benefits of removing assert* seem clear for some (IIRC). All this lead to my previous sentence, which should read: isn't this *enourmous* discussion a clear sign that a lot of people *won't* find an API without fail* (or assert*) simpler? I agree there's trimming to be done, of course. I'm just saying that there's clearly no agreement on this particular point of whether to remove fail* completely, so maybe we should take it as as indication that it might not actually simplify the API (in the sense of how comfortable people are with using it).
If assert* to fail* mapping is consistent, then voilá, simple, everyone can remember method names (+1 for PEP-8 renaming), everyone is free to think of each test as they please.
Thanks for the feedback.
No problem! It's great to be in a community where these things are so openly discussed :) rbp -- Rodrigo Bernardo Pimentel <rbp@isnomore.net> | GPG: <0x0DB14978>
On 15-Jul-08, at 6:05 AM, Andrew Bennetts wrote:
Ben Finney wrote:
"Stephen J. Turnbull" <stephen@xemacs.org> writes:
That measured only usage of unittest *within the Python standard library*. Is that the only body of unittest-using code we need consider?
Three more data points then:
bzr: 13228 assert* vs. 770 fail*.
Twisted: 6149 assert* vs. 1666 fail*.
paramiko: 431 assert* vs. 4 fail*.
Our internal code base: $ ack self.assert. | wc -l 3232 $ ack self.fail. | wc -l 1124 -Mike
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@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 :
Ben Finney writes:
Removal of ``assert*`` names ----------------------------
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.
FWIW, I think these are fairly stated. So fairly that I'm surprised you haven't been persuaded!<wink> Nitpick: the second point is not just "precedent", there's an economic reason there too. Tests in the standard distribution which use the deprecated style will need to be converted. Steven d'Aprano claims this is nontrivial (and thus error- prone) in some cases. I haven't seen that claim denied, and it seems plausible to me.
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]_.
You should mention Raymond's concern that PEP 8 names are quite a bit longer.
On Tue, Jul 15, 2008 at 3:20 PM, Stephen J. Turnbull <stephen@xemacs.org> wrote:
* 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.
FWIW, I think these are fairly stated. So fairly that I'm surprised you haven't been persuaded!<wink> Nitpick: the second point is not just "precedent", there's an economic reason there too. Tests in the standard distribution which use the deprecated style will need to be converted. Steven d'Aprano claims this is nontrivial (and thus error- prone) in some cases. I haven't seen that claim denied, and it seems plausible to me.
I'd like to see examples of that (this would be Steven's task if he's serious about his assertion). Since the fail and assert names are mapped to each other using aliasing I don't see how it could be nontrivial to map e.g. self.failIf(x) to self.assertFalse(x) -- these are the same function! -- --Guido van Rossum (home page: http://www.python.org/~guido/)
On Wed, 16 Jul 2008 08:13:22 am Guido van Rossum wrote:
Tests in the standard distribution which use the deprecated style will need to be converted. Steven d'Aprano claims this is nontrivial (and thus error- prone) in some cases. I haven't seen that claim denied, and it seems plausible to me.
I'd like to see examples of that (this would be Steven's task if he's serious about his assertion). Since the fail and assert names are mapped to each other using aliasing I don't see how it could be nontrivial to map e.g. self.failIf(x) to self.assertFalse(x) -- these are the same function!
I have not knowingly claimed that mechanically swapping fail* to assert* tests was difficult. The difficulty I refer to is about readability and understanding of the code. I often think about tests as sequences of possible failures, and as such my unit tests are most naturally written as fail*. It is that mental effort of reversing the sense of the tests when reading and writing assert* tests that I refer to. If you want to declare that fail* must go, I'll be disappointed but life will go on. But despite the claims of those who have asserted (pun intended) that fail* tests are always more difficult to understand, that's not the case for everyone. -- Steven
"Stephen J. Turnbull" <stephen@xemacs.org> writes:
FWIW, I think these are fairly stated. So fairly that I'm surprised you haven't been persuaded!<wink>
I'm not persuaded because I find the arguments for 'fail*' more persuasive :-) I am, however, convinced that the consensus of the community is that 'assert*' is the right choice. The PEP will be revised accordingly.
Nitpick: the second point is not just "precedent", there's an economic reason there too. [...]
You should mention Raymond's concern that PEP 8 names are quite a bit longer.
Noted both of these, thanks. -- \ “The best way to get information on Usenet is not to ask a | `\ question, but to post the wrong information.” —Aahz | _o__) | Ben Finney
Stephen J. Turnbull wrote:
Ben Finney writes:
Removal of ``assert*`` names ----------------------------
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.
FWIW, I think these are fairly stated. So fairly that I'm surprised you haven't been persuaded!<wink> Nitpick: the second point is not just "precedent", there's an economic reason there too. Tests in the standard distribution which use the deprecated style will need to be converted. Steven d'Aprano claims this is nontrivial (and thus error- prone) in some cases. I haven't seen that claim denied, and it seems plausible to me.
I disagree with SdA's statement there: failUnless* maps directly to assert* and failIf* maps directly to assertNot*. There are no semantic changes whatsoever involved in that remapping, just a change in the method names. Note that if the API is to be rationalised at all, my personal preference is to keep assert* and failIf* and get rid of their longer counterparts (failUnless* and assertNot*). Alternatively, ditch the binary assertion methods entirely, and use a check object to state those assertions: check(x).almost_equal(y) instead assertAlmostEqual(x, y) Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia --------------------------------------------------------------- http://www.boredomandlaziness.org
On Tue, Jul 15, 2008 at 6:58 AM, Ben Finney <ben+python@benfinney.id.au> wrote:
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@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.
Is any provision being made for a 2to3 fixer/otherwise-automated transition for the changes you propose here? Collin Winter
Collin Winter wrote:
On Tue, Jul 15, 2008 at 6:58 AM, Ben Finney <ben+python@benfinney.id.au> wrote:
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.
Is any provision being made for a 2to3 fixer/otherwise-automated transition for the changes you propose here?
As the deprecation is intended for 2.X and 3.X - is 2to3 fixer needed? Michael
Collin Winter _______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/fuzzyman%40voidspace.org.u...
-- http://www.ironpythoninaction.com/ http://www.voidspace.org.uk/ http://www.trypython.org/ http://www.ironpython.info/ http://www.theotherdelia.co.uk/ http://www.resolverhacks.net/
Michael Foord wrote:
Collin Winter wrote:
Is any provision being made for a 2to3 fixer/otherwise-automated transition for the changes you propose here?
As the deprecation is intended for 2.X and 3.X - is 2to3 fixer needed?
A fixer will only be needed when it actually is needed, but when it is, it should be a unittest-name fixer since previous 3.x code will also need fixing. Since the duplicates are multiples names for the same objects, the fixer should be a trivial name substitution.
Terry Reedy wrote:
Michael Foord wrote:
Collin Winter wrote:
Is any provision being made for a 2to3 fixer/otherwise-automated transition for the changes you propose here?
As the deprecation is intended for 2.X and 3.X - is 2to3 fixer needed?
A fixer will only be needed when it actually is needed, but when it is, it should be a unittest-name fixer since previous 3.x code will also need fixing. Since the duplicates are multiples names for the same objects, the fixer should be a trivial name substitution.
Can 2to3 fixers be used for 2to2 and 3to3 translation then? Michael
_______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/fuzzyman%40voidspace.org.u...
-- http://www.ironpythoninaction.com/ http://www.voidspace.org.uk/ http://www.trypython.org/ http://www.ironpython.info/ http://www.theotherdelia.co.uk/ http://www.resolverhacks.net/
On Wed, Jul 16, 2008 at 5:21 AM, Michael Foord <fuzzyman@voidspace.org.uk> wrote:
Terry Reedy wrote:
Michael Foord wrote:
Collin Winter wrote:
Is any provision being made for a 2to3 fixer/otherwise-automated transition for the changes you propose here?
As the deprecation is intended for 2.X and 3.X - is 2to3 fixer needed?
A fixer will only be needed when it actually is needed, but when it is, it should be a unittest-name fixer since previous 3.x code will also need fixing. Since the duplicates are multiples names for the same objects, the fixer should be a trivial name substitution.
Can 2to3 fixers be used for 2to2 and 3to3 translation then?
The intention is for the infrastructure behind 2to3 to be generally reusable for other Python source-to-source translation tools, be that 2to2 or 3to3. That hasn't fully materialized yet, but it's getting there. Collin
On Tue, Jul 15, 2008 at 6:03 PM, Michael Foord <fuzzyman@voidspace.org.uk> wrote:
Collin Winter wrote:
On Tue, Jul 15, 2008 at 6:58 AM, Ben Finney <ben+python@benfinney.id.au> wrote:
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.
Is any provision being made for a 2to3 fixer/otherwise-automated transition for the changes you propose here?
As the deprecation is intended for 2.X and 3.X - is 2to3 fixer needed?
IMO some kind of automated transition tool is needed -- anyone who has the time to convert their codebase by hand (for some definition of "by hand" that involves sed) doesn't have enough to do. Collin
On 2008-07-16 02:20, Collin Winter wrote:
On Tue, Jul 15, 2008 at 6:58 AM, Ben Finney <ben+python@benfinney.id.au> wrote:
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@benfinney.id.au> :Status: Draft :Type: Standards Track :Content-Type: test/x-rst :Created: 2008-07-14 :Python-Version: 2.7, 3.1
+1 for doing this in 3.1. -1 for Python 2.7. The main reason is that there's just too much 2.x code out there using the API names you are suggesting to change and/or remove from the module. Since this is a major change in the unit test API, I'd also like to suggest that you use a new module name. This is both a precaution to prevent tests failing due to not having been upgraded and a way for old code to continue working by adding the old unittest module on sys.path. Please note that the required renaming of the methods in existing tests is not going to be as straight forward as you may think, since you may well rename method calls into the tested application rather than just the unit test class method calls if you're not careful.
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.
-- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Jul 16 2008)
Python/Zope Consulting and Support ... http://www.egenix.com/ mxODBC.Zope.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
:::: Try mxODBC.Zope.DA for Windows,Linux,Solaris,MacOSX for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611
"M.-A. Lemburg" <mal@egenix.com> writes:
Since this is a major change in the unit test API, I'd also like to suggest that you use a new module name.
This is both a precaution to prevent tests failing due to not having been upgraded and a way for old code to continue working by adding the old unittest module on sys.path.
Do you have a specific argument against the provisions already stated in the PEP for backward compatibility? They seem to address your concerns already. -- \ “If you don't know what your program is supposed to do, you'd | `\ better not start writing it.” —Edsger W. Dijkstra | _o__) | Ben Finney
On 2008-07-16 10:14, Ben Finney wrote:
"M.-A. Lemburg" <mal@egenix.com> writes:
Since this is a major change in the unit test API, I'd also like to suggest that you use a new module name.
This is both a precaution to prevent tests failing due to not having been upgraded and a way for old code to continue working by adding the old unittest module on sys.path.
Do you have a specific argument against the provisions already stated in the PEP for backward compatibility? They seem to address your concerns already.
The PEP doesn't mention changing the module name and deprecating the old one. Instead it wants to deprecate all the old names (and cites PEP 4 for this), but keeping the module name. Note that PEP 4 targets deprecating use of whole modules, not single APIs, or - like in your case - more or less the complete existing API of a module. Given the scope of the changes, you are really creating a completely new API and as a result should also get a new module name. You can then deprecate use of the old "unittest" module name and point users to the new one. Developers who don't feel like changing 10000+ tests can then continue to use the old module and start using the new module for new projects. -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Jul 16 2008)
Python/Zope Consulting and Support ... http://www.egenix.com/ mxODBC.Zope.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
:::: Try mxODBC.Zope.DA for Windows,Linux,Solaris,MacOSX for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611
M.-A. Lemburg wrote:
On 2008-07-16 10:14, Ben Finney wrote:
"M.-A. Lemburg" <mal@egenix.com> writes:
Since this is a major change in the unit test API, I'd also like to suggest that you use a new module name.
This is both a precaution to prevent tests failing due to not having been upgraded and a way for old code to continue working by adding the old unittest module on sys.path.
Do you have a specific argument against the provisions already stated in the PEP for backward compatibility? They seem to address your concerns already.
The PEP doesn't mention changing the module name and deprecating the old one. Instead it wants to deprecate all the old names (and cites PEP 4 for this), but keeping the module name.
Note that PEP 4 targets deprecating use of whole modules, not single APIs, or - like in your case - more or less the complete existing API of a module.
Which PEP is usually referenced for the deprecation of individual APIs?
Given the scope of the changes, you are really creating a completely new API and as a result should also get a new module name. You can then deprecate use of the old "unittest" module name and point users to the new one.
You propose that we duplicate the entire module with a new name, maintaining both in parallel but with different method names? That doesn't sound wise to me.
Developers who don't feel like changing 10000+ tests can then continue to use the old module and start using the new module for new projects.
So we shouldn't bring the API inline with PEP 8 because it is widely used? Even if it causes some pain (and the methods won't be removed for several years if we follow the normal deprecation schedule), the fact that the API is widely used would seem to be an argument in favor of making it follow the Python style guidelines. Michael -- http://www.ironpythoninaction.com/ http://www.voidspace.org.uk/ http://www.trypython.org/ http://www.ironpython.info/ http://www.theotherdelia.co.uk/ http://www.resolverhacks.net/
On 2008-07-16 14:02, Michael Foord wrote:
M.-A. Lemburg wrote:
On 2008-07-16 10:14, Ben Finney wrote:
"M.-A. Lemburg" <mal@egenix.com> writes:
Since this is a major change in the unit test API, I'd also like to suggest that you use a new module name.
This is both a precaution to prevent tests failing due to not having been upgraded and a way for old code to continue working by adding the old unittest module on sys.path.
Do you have a specific argument against the provisions already stated in the PEP for backward compatibility? They seem to address your concerns already.
The PEP doesn't mention changing the module name and deprecating the old one. Instead it wants to deprecate all the old names (and cites PEP 4 for this), but keeping the module name.
Note that PEP 4 targets deprecating use of whole modules, not single APIs, or - like in your case - more or less the complete existing API of a module.
Which PEP is usually referenced for the deprecation of individual APIs?
PEP 5 could be used for that. Adding several 10s of deprecation warnings to the unittest module is not going to make life easier for anyone. Adding just a single one on import and following PEP 4 is. If you do want to apply major changes to a module without changing the name, then this could be done as part of the 2.x -> 3.x transition. The 2.x branch is not the right place for such breakage.
Given the scope of the changes, you are really creating a completely new API and as a result should also get a new module name. You can then deprecate use of the old "unittest" module name and point users to the new one.
You propose that we duplicate the entire module with a new name, maintaining both in parallel but with different method names?
No, I'm proposing to apply all the name changes to a new module and deprecate the old one. unittest will then go unmaintained until it is removed.
That doesn't sound wise to me.
Developers who don't feel like changing 10000+ tests can then continue to use the old module and start using the new module for new projects.
So we shouldn't bring the API inline with PEP 8 because it is widely used?
I didn't say that. However, if it's not required, then breaking a complete module API isn't necessary - "practicality beats purity". Instead add a new module with all the changes and have developers gradually migrate to the new code.
Even if it causes some pain (and the methods won't be removed for several years if we follow the normal deprecation schedule), the fact that the API is widely used would seem to be an argument in favor of making it follow the Python style guidelines.
So you're saying that because many people use the code, we should be more inclined to make their life harder. That's an interesting argument :-) -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Jul 16 2008)
Python/Zope Consulting and Support ... http://www.egenix.com/ mxODBC.Zope.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
:::: Try mxODBC.Zope.DA for Windows,Linux,Solaris,MacOSX for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611
"M.-A. Lemburg" <mal@egenix.com> writes:
On 2008-07-16 14:02, Michael Foord wrote:
M.-A. Lemburg wrote:
Note that PEP 4 targets deprecating use of whole modules, not single APIs, or - like in your case - more or less the complete existing API of a module.
Which PEP is usually referenced for the deprecation of individual APIs?
PEP 5 could be used for that.
That seems an even worse fit; it speaks of changing language features, not library modules. At least PEP 4 talks about when to raise DeprecationWarning.
Adding several 10s of deprecation warnings to the unittest module is not going to make life easier for anyone. Adding just a single one on import and following PEP 4 is.
I don't see how the first "is not going to make life easier" if the second somehow is. Is a programmer going to be helpless in the face of some DeprecationWarnings but not others?
If you do want to apply major changes to a module without changing the name, then this could be done as part of the 2.x -> 3.x transition.
This has already been rejected <URL:http://mail.python.org/pipermail/python-dev/2008-April/078485.html>. I'm inclined to agree that it's not right for 2.x. I'll revise the PEP accordingly. -- \ “First they came for the verbs, and I said nothing, for verbing | `\ weirds language. Then, they arrival for the nouns and I speech | _o__) nothing, for I no verbs.” —Peter Ellis | Ben Finney
On 2008-07-16 15:12, Ben Finney wrote:
"M.-A. Lemburg" <mal@egenix.com> writes:
On 2008-07-16 14:02, Michael Foord wrote:
M.-A. Lemburg wrote:
Note that PEP 4 targets deprecating use of whole modules, not single APIs, or - like in your case - more or less the complete existing API of a module. Which PEP is usually referenced for the deprecation of individual APIs? PEP 5 could be used for that.
That seems an even worse fit; it speaks of changing language features, not library modules. At least PEP 4 talks about when to raise DeprecationWarning.
Right and the methods described there are usually also applied to language changes and API changes. I just wanted to make clear that your "...see PEP 4 for how to handle backwards compatibility..." statement doesn't apply to the changes described in your PEP. However, it does point at a possible compromise which would make the transition easier on everyone.
Adding several 10s of deprecation warnings to the unittest module is not going to make life easier for anyone. Adding just a single one on import and following PEP 4 is.
I don't see how the first "is not going to make life easier" if the second somehow is. Is a programmer going to be helpless in the face of some DeprecationWarnings but not others?
Using the first method (changing the API names), you force developers to change existing code, which results in testing the test code. Lots of work with no real benefit. With the second method, they can use the new names with new test code (which then imports the new module). They don't have to test their existing tests for obscure search&replace errors.
If you do want to apply major changes to a module without changing the name, then this could be done as part of the 2.x -> 3.x transition.
This has already been rejected <URL:http://mail.python.org/pipermail/python-dev/2008-April/078485.html>
I wasn't suggesting to apply to the change to 3.0, but instead suggesting that if you want to implement such a major API change, this should be done only on the 3.x branch and be dealt with in the 2to3 tool.
I'm inclined to agree that it's not right for 2.x. I'll revise the PEP accordingly.
Thanks, -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Jul 16 2008)
Python/Zope Consulting and Support ... http://www.egenix.com/ mxODBC.Zope.Database.Adapter ... http://zope.egenix.com/ mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/
:::: Try mxODBC.Zope.DA for Windows,Linux,Solaris,MacOSX for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611
"M.-A. Lemburg" <mal@egenix.com> writes:
The PEP doesn't mention changing the module name and deprecating the old one.
Right. The intention is to have a PEP-8-conformant 'unittest' module, not an entirely new module.
Instead it wants to deprecate all the old names (and cites PEP 4 for this), but keeping the module name.
Yes, because all the existing documented API is retained as is, just with new names that conform with PEP 8.
Note that PEP 4 targets deprecating use of whole modules, not single APIs, or - like in your case - more or less the complete existing API of a module.
This is true, I tried to be specific about what was to be done to deprecate individual attributes, and could only find PEP 4. Is there a better reference for deprecation of Python features?
Given the scope of the changes, you are really creating a completely new API and as a result should also get a new module name.
I disagree. The API is not "completely new"; it has exactly the same functionality and expected behaviour, just with a different set of names.
Developers who don't feel like changing 10000+ tests can then continue to use the old module and start using the new module for new projects.
I agree with Michael Foord that an extensively-used standard library module is an argument in favour of re-working that module to be in line with the standard library guidelines. The change is, as has been pointed out elsewhere, a replacement of one set of names with another, retaining exactly the same expected behaviour. It's not on the scale of deprecating usage of an entire module. -- \ “I went to the museum where they had all the heads and arms | `\ from the statues that are in all the other museums.” —Steven | _o__) Wright | Ben Finney
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 M.-A. Lemburg wrote:
On 2008-07-16 02:20, Collin Winter wrote:
On Tue, Jul 15, 2008 at 6:58 AM, Ben Finney <ben+python@benfinney.id.au> wrote:
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@benfinney.id.au> :Status: Draft :Type: Standards Track :Content-Type: test/x-rst :Created: 2008-07-14 :Python-Version: 2.7, 3.1
+1 for doing this in 3.1.
-1 for Python 2.7.
The main reason is that there's just too much 2.x code out there using the API names you are suggesting to change and/or remove from the module.
Since this is a major change in the unit test API, I'd also like to suggest that you use a new module name.
This is both a precaution to prevent tests failing due to not having been upgraded and a way for old code to continue working by adding the old unittest module on sys.path.
Please note that the required renaming of the methods in existing tests is not going to be as straight forward as you may think, since you may well rename method calls into the tested application rather than just the unit test class method calls if you're not careful.
+1. I had just groped my way to that counter-proposal myself, for exactly your reasons. Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tseaver@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org iD8DBQFIfk+W+gerLs4ltQ4RApZlAJ47NVKXxbL/oaYyVZEUgRnnvajm+wCgyOO2 4GbVo2D1eWYcJvpx1yf8bLs= =2HV6 -----END PGP SIGNATURE-----
Tres Seaver wrote:
-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1
M.-A. Lemburg wrote:
On 2008-07-16 02:20, Collin Winter wrote:
On Tue, Jul 15, 2008 at 6:58 AM, Ben Finney <ben+python@benfinney.id.au> wrote:
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@benfinney.id.au> :Status: Draft :Type: Standards Track :Content-Type: test/x-rst :Created: 2008-07-14 :Python-Version: 2.7, 3.1
+1 for doing this in 3.1.
-1 for Python 2.7.
The main reason is that there's just too much 2.x code out there using the API names you are suggesting to change and/or remove from the module.
Since this is a major change in the unit test API, I'd also like to suggest that you use a new module name.
This is both a precaution to prevent tests failing due to not having been upgraded and a way for old code to continue working by adding the old unittest module on sys.path.
Please note that the required renaming of the methods in existing tests is not going to be as straight forward as you may think, since you may well rename method calls into the tested application rather than just the unit test class method calls if you're not careful.
Do you have production code methods called 'assertEquals' and the like? It sounds pretty unlikely to me. Michael
+1. I had just groped my way to that counter-proposal myself, for exactly your reasons.
Tres. - -- =================================================================== Tres Seaver +1 540-429-0999 tseaver@palladion.com Palladion Software "Excellence by Design" http://palladion.com -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFIfk+W+gerLs4ltQ4RApZlAJ47NVKXxbL/oaYyVZEUgRnnvajm+wCgyOO2 4GbVo2D1eWYcJvpx1yf8bLs= =2HV6 -----END PGP SIGNATURE-----
_______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/fuzzyman%40voidspace.org.u...
-- http://www.ironpythoninaction.com/ http://www.voidspace.org.uk/ http://www.trypython.org/ http://www.ironpython.info/ http://www.theotherdelia.co.uk/ http://www.resolverhacks.net/
Significant changes are resolving to remove the 'fail*' names, giving recommended name to use for each removed name, and further summary of related discussion. :PEP: XXX :Title: Consolidating names in the `unittest` module :Version: 0.3 :Last-Modified: 2008-07-16 :Author: Ben Finney <ben+python@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 ~~~~~~~~~~~~~~~~~~~~~~~ ``assert_`` Use ``assert_true``, or an ``assert`` statement. ``assertEquals`` Use ``assert_equal``. ``assertNotEquals`` Use ``assert_not_equal``. ``assertAlmostEquals`` Use ``assert_almost_equal``. ``assertNotAlmostEquals`` Use ``assert_not_almost_equal``. ``failIf`` Use ``assert_false``. ``failUnless`` Use ``assert_true``. ``failIfAlmostEqual`` Use ``assert_not_almost_equal``. ``failIfEqual`` Use ``assert_not_equal``. ``failUnlessAlmostEqual`` Use ``assert_almost_equal``. ``failUnlessEqual`` Use ``assert_equal``. ``failUnlessRaises`` Use ``assert_raises``. 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(…)`` ``assert_true(…)`` Replaces ``assertTrue(…)`` ``assert_false(…)`` Replaces ``assertFalse(…)`` ``assert_almost_equal(…)`` Replaces ``assertAlmostEqual(…)`` ``assert_equal(…)`` Replaces ``assertEqual(…)`` ``assert_not_almost_equal(…)`` Replaces ``assertNotAlmostEqual(…)`` ``assert_not_equal(…)`` Replaces ``assertNotEqual(…)`` ``assert_raises(exc_class, callable_obj, *args, **kwargs)`` Replaces ``assertRaises(excClass, callableObj, *args, **kwargs)`` ``run_test(…)`` Replaces ``runTest(…)`` ``setup(…)`` Replaces ``setUp(…)`` ``short_description(…)`` Replaces ``shortDescription(…)`` ``teardown(…)`` 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 ``fail*`` 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 (it was several times characterised as "bike-shedding" [#bikeshed]_ by the BDFL and others). With good arguments made in favour of each of "retain ``assert*``" and "retain ``fail*``", this PEP resolves in favour of stated BDFL preference, and de facto community preference. 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]_, [#seaver-1]_. * Economic: The above precedent indicates a simple economic argument [#turnbull-1]_: the majority of tests will not need their statements re-phrased, so updating in favour of them will involve less disruption and risk of error. * 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. It has been argued [#thomas-1]_ that newcomers to `unittest` who already know what ``assert`` does can be confused by the behaviour of the ``assert*`` names. This confusion does not exist for the ``fail*`` names, which don't conflict with existing concepts. * Avoid name collision: The above confusion with ``assert`` 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]_. While argument was raised [#hettinger-1]_ over the length of names resulting from a simple conversion of names to multi_word_with_underscores, this PEP chooses names that are consistent in wording with the existing names. An exception was made in the case of ``setup`` and ``teardown``. The two-word form of these names was judged [#hettinger-1]_ too cumbersome compared to the new single-word form. 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 .. [#bikeshed] http://www.catb.org/~esr/jargon/html/B/bikeshedding.html .. [#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 .. [#seaver-1] http://mail.python.org/pipermail/python-dev/2008-July/081166.html .. [#thomas-1] http://mail.python.org/pipermail/python-dev/2008-July/081153.html .. [#hettinger-1] http://mail.python.org/pipermail/python-dev/2008-July/081107.html .. [#turnbull-1] http://mail.python.org/pipermail/python-dev/2008-July/081175.html .. Local Variables: mode: rst coding: utf-8 End: vim: filetype=rst :
Significant changes: now targets only Python 3.1, and recording the new status (and rationale) as rejected by BDFL pronouncement. Feel free to mine for ideas. :PEP: XXX :Title: Consolidating names in the `unittest` module :Version: 0.4 :Last-Modified: 2008-07-17 :Author: Ben Finney <ben+python@benfinney.id.au> :Status: Rejected :Type: Standards Track :Content-Type: test/x-rst :Created: 2008-07-14 :Python-Version: 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 functions 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`_, 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 `PEP 20`_. 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 ~~~~~~~~~~~~~~~~~~~~~~~ ``assert_`` Use ``assert_true``, or an ``assert`` statement. ``assertEquals`` Use ``assert_equal``. ``assertNotEquals`` Use ``assert_not_equal``. ``assertAlmostEquals`` Use ``assert_almost_equal``. ``assertNotAlmostEquals`` Use ``assert_not_almost_equal``. ``failIf`` Use ``assert_false``. ``failUnless`` Use ``assert_true``. ``failIfAlmostEqual`` Use ``assert_not_almost_equal``. ``failIfEqual`` Use ``assert_not_equal``. ``failUnlessAlmostEqual`` Use ``assert_almost_equal``. ``failUnlessEqual`` Use ``assert_equal``. ``failUnlessRaises`` Use ``assert_raises``. 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`_. 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(…)`` ``assert_true(…)`` Replaces ``assertTrue(…)`` ``assert_false(…)`` Replaces ``assertFalse(…)`` ``assert_almost_equal(…)`` Replaces ``assertAlmostEqual(…)`` ``assert_equal(…)`` Replaces ``assertEqual(…)`` ``assert_not_almost_equal(…)`` Replaces ``assertNotAlmostEqual(…)`` ``assert_not_equal(…)`` Replaces ``assertNotEqual(…)`` ``assert_raises(exc_class, callable_obj, *args, **kwargs)`` Replaces ``assertRaises(excClass, callableObj, *args, **kwargs)`` ``run_test(…)`` Replaces ``runTest(…)`` ``setup(…)`` Replaces ``setUp(…)`` ``short_description(…)`` Replaces ``shortDescription(…)`` ``teardown(…)`` 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 ========= BDFL pronouncement ------------------ The BDFL has pronounced [#vanrossum-2]_ that no API renaming is to occur in the `unittest` module. This PEP is therefore rejected. Introduction after migration to Python 3.0 ------------------------------------------ The proposal to introduce these changes along with the migration to Python 3.0 is specifically not approved by the BDFL [#vanrossum-1]_, has many detractors, and has little definite support. This PEP therefore aims for introduction no earlier than Python 3.1. 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`_ ("there should be one, and preferably only one, obvious way to do it"). Removal of ``fail*`` 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 (it was several times characterised as "bike-shedding" [#bikeshed]_ by the BDFL and others). With good arguments made in favour of each of "retain ``assert*``" and "retain ``fail*``", this PEP resolves in favour of stated BDFL preference, and de facto community preference. 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]_, [#seaver-1]_. * Economic: The above precedent indicates a simple economic argument [#turnbull-1]_: the majority of tests will not need their statements re-phrased, so updating in favour of them will involve less disruption and risk of error. * 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. It has been argued [#thomas-1]_ that newcomers to `unittest` who already know what ``assert`` does can be confused by the behaviour of the ``assert*`` names. This confusion does not exist for the ``fail*`` names, which don't conflict with existing concepts. * Avoid name collision: The above confusion with ``assert`` 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`_. While argument was raised [#hettinger-1]_ over the length of names resulting from a simple conversion of names to multi_word_with_underscores, this PEP chooses names that are consistent in wording with the existing names. An exception was made in the case of ``setup`` and ``teardown``. The two-word form of these names was judged [#hettinger-1]_ too cumbersome compared to the new single-word form. Backwards Compatibility ======================= The names to be obsoleted should be deprecated and removed according to the schedule for modules in `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 .. [#bikeshed] http://www.catb.org/~esr/jargon/html/B/bikeshedding.html .. [#vanrossum-1] http://mail.python.org/pipermail/python-dev/2008-April/078485.html .. [#vanrossum-2] http://mail.python.org/pipermail/python-dev/2008-July/081263.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 .. [#seaver-1] http://mail.python.org/pipermail/python-dev/2008-July/081166.html .. [#thomas-1] http://mail.python.org/pipermail/python-dev/2008-July/081153.html .. [#hettinger-1] http://mail.python.org/pipermail/python-dev/2008-July/081107.html .. [#turnbull-1] http://mail.python.org/pipermail/python-dev/2008-July/081175.html .. Local Variables: mode: rst coding: utf-8 End: vim: filetype=rst :
participants (29)
-
Andrew Bennetts -
Antoine Pitrou -
Ben Finney -
Ben Finney -
Benjamin Peterson -
C. Titus Brown -
Collin Winter -
Eric Smith -
Gerard flanagan -
Greg Ewing -
Guido van Rossum -
Janzert -
Jeroen Ruigrok van der Werven -
Jonathan Lange -
M.-A. Lemburg -
Michael Foord -
Mike Klaas -
Nick Coghlan -
Paul Moore -
Paul Rudin -
Raymond Hettinger -
Richard Thomas -
Rodrigo Bernardo Pimentel -
Scott Dial -
Stephen J. Turnbull -
Steve Holden -
Steven D'Aprano -
Terry Reedy -
Tres Seaver