[TIP] RFC: additional data in unittest TestResult outcomes
Michael Foord
fuzzyman at voidspace.org.uk
Tue Sep 22 03:31:34 PDT 2009
This is really interesting Robert, but there is a lot of detail. Can you
provide an abstract / summary?
It seems to be a protocol for object serialisation on test results
composed of several parts.
Methods on TestResult for attaching information.
Standard ways of storing that information.
A text serialisation protocol for communicating result information.
Is that correct?
Michael
Robert Collins wrote:
> So, this is one of a number of things I need to raise for discussion; I
> think the aggregate set should become a PEP for unittest improvements
> and be taken to python-dev. However, I want to be sure that it works
> first :)
>
> I've been meaning to write a single compelling argument for these
> things, but I think the perfect has become the enemy of the good; so I'm
> going to simply discuss them piecemeal as time & reminders permit.
>
> In unittest, when a test has an outcome (e.g. it failed) it calls
> result.testOutcome(test, ...)
>
> depending on the outcome, the ... might be empty, a textual reason, or a
> backtrace triple.
>
> This is easy to use and all, but its rather limiting. A common thing I
> see unittest extensions do is to overload startTest and stopTest to
> capture stdout and stderr. Another way that that is sometimes done is
> inspect the test object for magic attributes which have log files or
> other additional data.
>
> I think we should make the protocol for doing this be part of the core.
>
> The simplest thing I've come up with so far is to simply allow an
> arbitrary number of data for a test. So, a TestCase might say:
> result.testFailedExtended(self, {'traceback': self.exc_info(), 'log':
> self.log_contents(), 'stdout': self.captured_stdout.getvalue()})
>
> I think this would be very easy for results to deal with.
> _TextTestResult would simply stringify each item when running in verbose
> mode. A GUI test runner could display the keys of the dict in a tree
> view. Elements it knows about it could look for and handle specially.
>
> More importantly not every bit of data a test can gather [and provide
> for debugging failures] makes sense as a string. For instance,
> callgraphs are argurable best presented as kcachegrind files, for
> loading into kcachegrind. Likewise a DB or B+Tree from a failed run is
> binary and useful to examine on disk and with external tools, not as
> something to show in the console.
>
> If we want to be able to ship test results over the network (as py.test,
> Pandokia, Subunit and others either do, or enable the doing-of), then we
> should in our object protocol support this. So I think it would be great
> to add a little bit more metadata to allow these use cases without
> utterly swamping someone watching an interactive test run.
>
> There is some prior art here - Pandokia uses a flat namespace of
> strings. bzr's test runner uses objects (it does the 'ask the test case
> for extended data' trick). Drizzle leaves debug directories on disk. I'm
> not sure what py.test does.
>
> In the interests of getting it wrong I suggest using a content object
> rather than a plain string; giving that object a content-type for now,
> and seeing how it goes/if people like it. If its too limiting we can add
> a content-encoding; if its too broad we can talk about just-strings.
>
> What would be _really cool_ is if the py.test, nose & Pandokia folk can
> comment on whether this:
> - fits with what they do [even if its spelt differently today]
> - is too broad or too limiting.
>
> Implementation-wise, as far as I can see neither wsgi nor
> SimpleHTTPServer actually define a Content object type, so we can't just
> reuse. The email Mime support is all about messages; perhaps it would be
> appropriate to use, but it doesn't really look like it to me.
>
> The goal in the code below is to allow:
> - in python processing to be clear and introspect tracebacks as
> some TestResults may do today. If we think no TestResults actually
> do that today, then we can simply say that all information about a
> test outcome has to be stringlike (but still attach content types to
> permit sensible display without guessing)
> - allow other in python processing to be equally nice
> - allow extended processing and integration with external services
> like Pandokia to be easy to achieve.
>
> class ContentType(object):
> """A content type from http://www.iana.org/assignments/media-types/
>
> :ivar type: The primary type, e.g. "text" or "application"
> :ivar subtype: The subtype, e.g. "plain" or "octet-stream"
> :ivar parameters: A dict of additional parameters specific to the
> content type.
> """
>
> def __init__(self, primary_type, subtype, parameters=None):
> ...
>
> class Content(object):
> """Content that has been attached to a test outcome.
>
> :ivar content_type: The content type of this Content.
> """
>
> def iter_bytes(self):
> """Return an iterator over bytestrings for this content."""
>
>
> class Traceback(Content):
> """Python backtraces.
>
> :ivar exc_info: The sys.exc_info for the exception.
> :ivar _failureException: The Exception the generating test used
> for raising Assertions.
> """
>
> def __init__(self, exc_info, failureException):
> self.content_type = ContentType("text", "x-python-traceback")
> self.exc_info = exc_info
> self._failureException = failureException
>
> def _exc_info_to_string(self):
> exctype, value, tb = self.exc_info
> # Skip test runner traceback levels
> while tb and self._is_relevant_tb_level(tb):
> tb = tb.tb_next
> if exctype is self._failureException:
> # Skip assert*() traceback levels
> length = self._count_relevant_tb_levels(tb)
> return ''.join(traceback.format_exception(exctype, value,
> tb, length))
> return ''.join(traceback.format_exception(exctype, value, tb))
>
> def iter_bytes(self):
> return [self._exc_info_to_string()]
>
>
> compatibility
> =============
>
> TestCase objects calling Extended outcomes should handle the outcome
> method being absent and fallback to calling the existing matching
> outcome. The original outcome should not be called if the Extended one
> existed.
>
> So - thoughts?
>
> In case its not obvious, I'm volunteering to see how this works in
> practice in at least a few places. Concretely, one of the issues
> reported by Ronny Pfannschmidt in getting Subunit <-> Nose integration
> was in being able to do the stdout/stderr capture (and Subunit's
> outcomes are an exact mapping of unittests where the outcome exists at
> all, something I want to keep)... so this, to me, is an ideal test case.
>
> -Rob
>
> ------------------------------------------------------------------------
>
> _______________________________________________
> testing-in-python mailing list
> testing-in-python at lists.idyll.org
> http://lists.idyll.org/listinfo/testing-in-python
>
--
http://www.ironpythoninaction.com/
More information about the testing-in-python
mailing list