[Python-Dev] PEP: Frequently-requested additional features for the unittest
module (original) (raw)
Ben Finney ben+python at benfinney.id.au
Wed Jul 16 03:19:44 CEST 2008
- Previous message: [Python-Dev] Bug 3139
- Next message: [Python-Dev] PEP: Frequently-requested additional features for the `unittest` module
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
:PEP: XXX
:Title: Frequently-requested additional features for the unittest
module
:Version: 0.3
:Last-Modified: 2008-07-16
:Author: Ben Finney <ben+python at benfinney.id.au>
:Status: Draft
:Type: Standards Track
:Content-Type: test/x-rst
:Requires: PEP XXX (Consolidating names in the unittest
module)
:Created: 2008-07-14
:Post-History:
.. contents::
Abstract
This PEP proposes frequently-requested additions to the standard
library unittest
module that are natural extensions of its existing
functionality.
Motivation
The unittest
module is functionally complete. Nevertheless, many
users request and devise extensions to perform functions that are so
common they deserve to be in the standard module.
Specification
New condition tests
The following test methods will be added to the TestCase
class.
The function signature is part of this specification. The body is to
be treated as a reference implementation only; any functionally
identical implementation also meets this specification.
:: import operator import re
class TestCase(object):
# …
def assert_compare_true(op, first, second, msg=None):
if msg is None:
msg = "%(first)r %(op)r %(second)" % vars()
if not op(first, second):
raise self.failure_exception(msg)
def assert_in(container, member, msg=None):
op = operator.__contains__
self.assert_compare_true(op, container, member, msg)
def assert_is(first, second, msg=None):
op = operator.is_
self.assert_compare_true(op, first, second, msg)
def assert_less_than(first, second, msg=None):
op = operator.lt
self.assert_compare_true(op, first, second, msg)
def assert_greater_than(first, second, msg=None):
op = operator.gt
self.assert_compare_true(op, first, second, msg)
def assert_less_than_or_equal(first, second, msg=None):
op = operator.le
self.assert_compare_true(op, first, second, msg)
def assert_greater_than_or_equal(first, second, msg=None):
op = operator.ge
self.assert_compare_true(op, first, second, msg)
def assert_members_equal(first, second, msg=None):
self.assert_equal(set(first), set(second), msg)
def assert_sequence_equal(first, second, msg=None):
self.assert_equal(list(first), list(second), msg)
def assert_raises_with_message_regex(
exc_class, message_regex, callable_obj, *args, **kwargs):
exc_name = exc_class.__name__
message_pattern = re.compile(message_regex)
try:
callable_obj(*args, **kwargs)
except exc_class, exc:
exc_message = str(exc)
if not message_pattern.match(exc_message):
msg = (
"%(exc_name)s raised"
" without message matching %(message_regex)r"
" (got message %(exc_message)r)"
) % vars()
raise self.failure_exception(msg)
else:
msg = "%(exc_name)s not raised" % vars()
raise self.failure_exception(msg)
The following test methods are also added. Their implementation in each case is simply the logical inverse of a corresponding method above.
:: def assert_compare_false(op, first, second, msg=None): # Logical inverse of assert_compare_true
def assert_not_in(container, member, msg=None):
# Logical inverse of assert_in
def assert_is_not(first, second, msg=None)
# Logical inverse of assert_is
def assert_not_less_than(first, second, msg=None)
# Logical inverse of assert_less_than
def assert_not_greater_than(first, second, msg=None)
# Logical inverse of assert_greater_than
def assert_not_less_than_or_equal(first, second, msg=None)
# Logical inverse of assert_less_than_or_equal
def assert_not_greater_than_or_equal(first, second, msg=None)
# Logical inverse of assert_greater_than_or_equal
def assert_members_not_equal(first, second, msg=None)
# Logical inverse of assert_members_equal
def assert_sequence_not_equal(first, second, msg=None)
# Logical inverse of assert_sequence_equal
Enhanced failure message for equality tests
The equality tests will change their behaviour such that the message always, even if overridden with a specific message when called, includes extra information:
For both
assert_equal
andassert_not_equal
: therepr()
output of the objects that were compared.For
assert_equal
comparisons ofbasestring
instances that are multi-line text: the output ofdiff
comparing the two texts.For membership comparisons with
assert_*_equal
: therepr()
output of the members that were not equal in each collection. (This change is not done for the correspondingassert_*_not_equal
tests, which only fail if the collection members are equal.)
Simple invocation of test collection
The following new functionality will be added to the unittest
module. The function signature for run_tests
is part of this
specification; the implementation is to be considered a reference
implementation only.
:: def run_tests( tests, loader_class=TestLoader, suite_class=TestSuite, runner_class=TextTestRunner): """ Run a collection of tests with a test runner
:param tests:
A sequence of objects that can contain test cases:
modules, `TestSuite` instances, or `TestCase`
subclasses.
:param loader_class:
The type of test loader to use for collecting tests
from the `tests` collection.
:param suite_class:
The type of test suite to use for accumulating the
collected test cases.
:param runner_class:
The type of test runner to instantiate for running the
collected test cases.
:return:
None.
"""
def iter_testcases_recursively(collection, loader):
# Flatten and iterate over collection, generating
# instances of TestCase
loader = loader_class()
suite = suite_class()
for testcase in iter_testcases_recursively(tests, loader):
suite.add_tests(testcase)
runner = runner_class()
runner.run(suite)
Rationale
Names for logical-inverse tests
The simple pattern established by assert_foo
having a logical
inverse named assert_not_foo
sometimes results in gramatically
awkward names. The following names were chosen in exception of this
pattern, in the interest of the API names being more intuitive:
assert_is_not
assert_members_not_equal
assert_sequence_not_equal
Order of method parameters
The methods assert_in
, assert_not_in
have the container as the
first parameter. This makes the grammatical object of the function
name come immediately after the function name: "Assert in
container
". This matches the convention set by the existing
assert_raises(exception, callable_obj, …)
"(Assert the code
raises exception
").
Backwards Compatibility
This PEP proposes only additional features. There are no backward-incompatible changes.
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 :
- Previous message: [Python-Dev] Bug 3139
- Next message: [Python-Dev] PEP: Frequently-requested additional features for the `unittest` module
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]