testtools-dev team mailing list archive
-
testtools-dev team
-
Mailing list archive
-
Message #00054
[Merge] lp:~jml/testtools/placeholder-test into lp:testtools
Jonathan Lange has proposed merging lp:~jml/testtools/placeholder-test into lp:testtools.
Requested reviews:
testtools developers (testtools-dev)
This branch adds a couple of TestCase-like objects that can be used to add results to a TestResult. One is for a successful test, the other is for an erroring test.
A good use case for ErrorHolder is trapping import errors from a test loader and adding them to the TestResult as failing tests.
It's debatable whether the placeholders should count as 1 test or 0 tests. I chose 1 because it's less weird, but Twisted choses 0.
I guess the tests, ideally, should be more like interface verification tests.
--
https://code.launchpad.net/~jml/testtools/placeholder-test/+merge/33232
Your team testtools developers is requested to review the proposed merge of lp:~jml/testtools/placeholder-test into lp:testtools.
=== modified file 'NEWS'
--- NEWS 2010-08-17 13:08:01 +0000
+++ NEWS 2010-08-20 15:42:48 +0000
@@ -7,6 +7,9 @@
Improvements
------------
+ * Added `PlaceHolder` and `ErrorHolder`, TestCase-like objects that can be
+ used to add results to a `TestResult`.
+
* 'Mismatch' now takes optional description and details parameters, so
custom Matchers aren't compelled to make their own subclass.
=== modified file 'testtools/__init__.py'
--- testtools/__init__.py 2010-07-29 18:48:37 +0000
+++ testtools/__init__.py 2010-08-20 15:42:48 +0000
@@ -5,9 +5,11 @@
__all__ = [
'clone_test_with_new_id',
'ConcurrentTestSuite',
+ 'ErrorHolder',
'ExtendedToOriginalDecorator',
'iterate_tests',
'MultiTestResult',
+ 'PlaceHolder',
'TestCase',
'TestResult',
'TextTestResult',
@@ -25,6 +27,8 @@
RunTest,
)
from testtools.testcase import (
+ ErrorHolder,
+ PlaceHolder,
TestCase,
clone_test_with_new_id,
skip,
=== modified file 'testtools/testcase.py'
--- testtools/testcase.py 2010-08-04 13:05:20 +0000
+++ testtools/testcase.py 2010-08-20 15:42:48 +0000
@@ -450,12 +450,99 @@
self.__teardown_called = True
+class PlaceHolder(object):
+ """A placeholder test.
+
+ `PlaceHolder` implements much of the same interface as `TestCase` and is
+ particularly suitable for being added to `TestResult`s.
+ """
+
+ def __init__(self, test_id, short_description=None):
+ """Construct a `PlaceHolder`.
+
+ :param test_id: The id of the placeholder test.
+ :param short_description: The short description of the place holder
+ test. If not provided, the id will be used instead.
+ """
+ self._test_id = test_id
+ self._short_description = short_description
+
+ def __call__(self, result=None):
+ return self.run(result=result)
+
+ def __repr__(self):
+ internal = [self._test_id]
+ if self._short_description is not None:
+ internal.append(self._short_description)
+ return "<%s.%s(%s)>" % (
+ self.__class__.__module__,
+ self.__class__.__name__,
+ ", ".join(map(repr, internal)))
+
+ def __str__(self):
+ return self.id()
+
+ def countTestCases(self):
+ return 1
+
+ def debug(self):
+ pass
+
+ def id(self):
+ return self._test_id
+
+ def run(self, result=None):
+ if result is None:
+ result = TestResult()
+ result.startTest(self)
+ result.addSuccess(self)
+ result.stopTest(self)
+
+ def shortDescription(self):
+ if self._short_description is None:
+ return self.id()
+ else:
+ return self._short_description
+
+
+class ErrorHolder(PlaceHolder):
+ """A placeholder test that will error out when run."""
+
+ failureException = None
+
+ def __init__(self, test_id, error, short_description=None):
+ """Construct an `ErrorHolder`.
+
+ :param test_id: The id of the test.
+ :param error: The exc info tuple that will be used as the test's error.
+ :param short_description: An optional short description of the test.
+ """
+ super(ErrorHolder, self).__init__(
+ test_id, short_description=short_description)
+ self._error = error
+
+ def __repr__(self):
+ internal = [self._test_id, self._error]
+ if self._short_description is not None:
+ internal.append(self._short_description)
+ return "<%s.%s(%s)>" % (
+ self.__class__.__module__,
+ self.__class__.__name__,
+ ", ".join(map(repr, internal)))
+
+ def run(self, result=None):
+ if result is None:
+ result = TestResult()
+ result.startTest(self)
+ result.addError(self, self._error)
+ result.stopTest(self)
+
+
# Python 2.4 did not know how to copy functions.
if types.FunctionType not in copy._copy_dispatch:
copy._copy_dispatch[types.FunctionType] = copy._copy_immutable
-
def clone_test_with_new_id(test, new_id):
"""Copy a TestCase, and give the copied test a new id.
=== modified file 'testtools/tests/test_testtools.py'
--- testtools/tests/test_testtools.py 2010-08-04 13:05:20 +0000
+++ testtools/tests/test_testtools.py 2010-08-20 15:42:48 +0000
@@ -1,4 +1,4 @@
-# Copyright (c) 2008 Jonathan M. Lange. See LICENSE for details.
+# Copyright (c) 2008-2010 Jonathan M. Lange. See LICENSE for details.
"""Tests for extensions to the base test library."""
@@ -6,6 +6,8 @@
import unittest
from testtools import (
+ ErrorHolder,
+ PlaceHolder,
TestCase,
clone_test_with_new_id,
content,
@@ -26,6 +28,167 @@
)
+class TestPlaceHolder(TestCase):
+
+ def makePlaceHolder(self, test_id="foo", short_description=None):
+ return PlaceHolder(test_id, short_description)
+
+ def test_id_comes_from_constructor(self):
+ # The id() of a PlaceHolder is whatever you pass into the constructor.
+ test = PlaceHolder("test id")
+ self.assertEqual("test id", test.id())
+
+ def test_shortDescription_is_id(self):
+ # The shortDescription() of a PlaceHolder is the id, by default.
+ test = PlaceHolder("test id")
+ self.assertEqual(test.id(), test.shortDescription())
+
+ def test_shortDescription_specified(self):
+ # If a shortDescription is provided to the constructor, then
+ # shortDescription() returns that instead.
+ test = PlaceHolder("test id", "description")
+ self.assertEqual("description", test.shortDescription())
+
+ def test_repr_just_id(self):
+ # repr(placeholder) shows you how the object was constructed.
+ test = PlaceHolder("test id")
+ self.assertEqual(
+ "<testtools.testcase.PlaceHolder(%s)>" % repr(test.id()),
+ repr(test))
+
+ def test_repr_with_description(self):
+ # repr(placeholder) shows you how the object was constructed.
+ test = PlaceHolder("test id", "description")
+ self.assertEqual(
+ "<testtools.testcase.PlaceHolder(%r, %r)>" % (
+ test.id(), test.shortDescription()),
+ repr(test))
+
+ def test_counts_as_one_test(self):
+ # A placeholder test counts as one test.
+ test = self.makePlaceHolder()
+ self.assertEqual(1, test.countTestCases())
+
+ def test_str_is_id(self):
+ # str(placeholder) is always the id(). We are not barbarians.
+ test = self.makePlaceHolder()
+ self.assertEqual(test.id(), str(test))
+
+ def test_runs_as_success(self):
+ # When run, a PlaceHolder test records a success.
+ test = self.makePlaceHolder()
+ log = []
+ test.run(LoggingResult(log))
+ self.assertEqual(
+ [('startTest', test), ('addSuccess', test), ('stopTest', test)],
+ log)
+
+ def test_call_is_run(self):
+ # A PlaceHolder can be called, in which case it behaves like run.
+ test = self.makePlaceHolder()
+ run_log = []
+ test.run(LoggingResult(run_log))
+ call_log = []
+ test(LoggingResult(call_log))
+ self.assertEqual(run_log, call_log)
+
+ def test_runs_without_result(self):
+ # A PlaceHolder can be run without a result, in which case there's no
+ # way to actually get at the result.
+ self.makePlaceHolder().run()
+
+ def test_debug(self):
+ # A PlaceHolder can be debugged.
+ self.makePlaceHolder().debug()
+
+
+class TestErrorHolder(TestCase):
+
+ def makeException(self):
+ try:
+ raise RuntimeError("danger danger")
+ except:
+ return sys.exc_info()
+
+ def makePlaceHolder(self, test_id="foo", error=None,
+ short_description=None):
+ if error is None:
+ error = self.makeException()
+ return ErrorHolder(test_id, error, short_description)
+
+ def test_id_comes_from_constructor(self):
+ # The id() of a PlaceHolder is whatever you pass into the constructor.
+ test = ErrorHolder("test id", self.makeException())
+ self.assertEqual("test id", test.id())
+
+ def test_shortDescription_is_id(self):
+ # The shortDescription() of a PlaceHolder is the id, by default.
+ test = ErrorHolder("test id", self.makeException())
+ self.assertEqual(test.id(), test.shortDescription())
+
+ def test_shortDescription_specified(self):
+ # If a shortDescription is provided to the constructor, then
+ # shortDescription() returns that instead.
+ test = ErrorHolder("test id", self.makeException(), "description")
+ self.assertEqual("description", test.shortDescription())
+
+ def test_repr_just_id(self):
+ # repr(placeholder) shows you how the object was constructed.
+ error = self.makeException()
+ test = ErrorHolder("test id", error)
+ self.assertEqual(
+ "<testtools.testcase.ErrorHolder(%r, %r)>" % (test.id(), error),
+ repr(test))
+
+ def test_repr_with_description(self):
+ # repr(placeholder) shows you how the object was constructed.
+ error = self.makeException()
+ test = ErrorHolder("test id", error, "description")
+ self.assertEqual(
+ "<testtools.testcase.ErrorHolder(%r, %r, %r)>" % (
+ test.id(), error, test.shortDescription()),
+ repr(test))
+
+ def test_counts_as_one_test(self):
+ # A placeholder test counts as one test.
+ test = self.makePlaceHolder()
+ self.assertEqual(1, test.countTestCases())
+
+ def test_str_is_id(self):
+ # str(placeholder) is always the id(). We are not barbarians.
+ test = self.makePlaceHolder()
+ self.assertEqual(test.id(), str(test))
+
+ def test_runs_as_error(self):
+ # When run, a PlaceHolder test records a success.
+ error = self.makeException()
+ test = self.makePlaceHolder(error=error)
+ log = []
+ test.run(LoggingResult(log))
+ self.assertEqual(
+ [('startTest', test),
+ ('addError', test, error),
+ ('stopTest', test)], log)
+
+ def test_call_is_run(self):
+ # A PlaceHolder can be called, in which case it behaves like run.
+ test = self.makePlaceHolder()
+ run_log = []
+ test.run(LoggingResult(run_log))
+ call_log = []
+ test(LoggingResult(call_log))
+ self.assertEqual(run_log, call_log)
+
+ def test_runs_without_result(self):
+ # A PlaceHolder can be run without a result, in which case there's no
+ # way to actually get at the result.
+ self.makePlaceHolder().run()
+
+ def test_debug(self):
+ # A PlaceHolder can be debugged.
+ self.makePlaceHolder().debug()
+
+
class TestEquality(TestCase):
"""Test `TestCase`'s equality implementation."""
Follow ups