Is it possible to get the results of a test (i.e. whether all assertions have passed) in a tearDown() method? I'm running Selenium scripts, and I'd like to do some reporting from inside tearDown(), however I don't know if this is possible.
相关问题
- how to define constructor for Python's new Nam
- streaming md5sum of contents of a large remote tar
- How to get the background from multiple images by
- Evil ctypes hack in python
- Correctly parse PDF paragraphs with Python
Inspired by scoffey’s answer, I decided to take mercilessnes to the next level, and have come up with the following.
It works in both vanilla unittest, and also when run via nosetests, and also works in Python versions 2.7, 3.2, 3.3, and 3.4 (I did not specifically test 3.0, 3.1, or 3.5, as I don’t have these installed at the moment, but if I read the source code correctly, it should work in 3.5 as well):
When run with
unittest
:When run with
nosetests
:Background
I started with this:
However, this only works in Python 2. In Python 3, up to and including 3.3, the control flow appears to have changed a bit: Python 3’s unittest package processes results after calling each test’s
tearDown()
method… this behavior can be confirmed if we simply add an extra line (or six) to our test class:Then just re-run the tests:
…and you will see that you get this as a result:
Now, compare the above to Python 2’s output:
Since Python 3 processes errors/failures after the test is torn down, we can’t readily infer the result of a test using
result.errors
orresult.failures
in every case. (I think it probably makes more sense architecturally to process a test’s results after tearing it down, however, it does make the perfectly valid use-case of following a different end-of-test procedure depending on a test’s pass/fail status a bit harder to meet…)Therefore, instead of relying on the overall
result
object, instead we can reference_outcomeForDoCleanups
as others have already mentioned, which contains the result object for the currently running test, and has the necessaryerrors
andfailrues
attributes, which we can use to infer a test’s status by the timetearDown()
has been called:This adds support for the early versions of Python 3.
As of Python 3.4, however, this private member variable no longer exists, and instead, a new (albeit also private) method was added:
_feedErrorsToResult
.This means that for versions 3.4 (and later), if the need is great enough, one can — very hackishly — force one’s way in to make it all work again like it did in version 2…
…provided, of course, all consumers of this class remember to
super(…, self).tearDown()
in their respectivetearDown
methods…Disclaimer: Purely educational, don’t try this at home, etc. etc. etc. I’m not particularly proud of this solution, but it seems to work well enough for the time being, and is the best I could hack up after fiddling for an hour or two on a Saturday afternoon…
CAVEAT: I have no way of double checking the following theory at the moment, being away from a dev box. So this may be a shot in the dark.
Perhaps you could check the return value of
sys.exc_info()
inside your tearDown() method, if it returns(None, None, None)
, you know the test case succeeded. Otherwise, you could use returned tuple to interrogate the exception object.See sys.exc_info documentation.
Another more explicit approach is to write a method decorator that you could slap onto all your test case methods that require this special handling. This decorator can intercept assertion exceptions and based on that modify some state in
self
allowing your tearDown method to learn what's up.Python 2.7.
You can also get result after unittest.main():
or use suite:
If you take a look at the implementation of
unittest.TestCase.run
, you can see that all test results are collected in the result object (typically aunittest.TestResult
instance) passed as argument. No result status is left in theunittest.TestCase
object.So there isn't much you can do in the
unittest.TestCase.tearDown
method unless you mercilessly break the elegant decoupling of test cases and test results with something like this:EDIT: This works for Python 2.6 - 3.3, (modified for new Python bellow).
It depends what kind of reporting you'd like to produce.
In case you'd like to do some actions on failure (such as generating a screenshots), instead of using
tearDown()
, you may achieve that by overridingfailureException
.For example:
This solution is for Python versions 2.7 to 3.7 (the highest current version), without any decorators or other modification in any code before
tearDown
. Everything works according to the builtin classification of results. Also skipped tests orexpectedFailure
are recognized correctly. It evaluates the result of the current test, not a summary of all tests passed so far. Compatible also with pytest.Comments: Only one or zero exceptions (error or failure) need be reported because not more can be expected before
tearDown
. The packageunittest
expects that a second exception can be raised by tearDown. Therefore the listserrors
andfailures
can contain only one or zero elements together before tearDown. Lines after "demo" comment are reporting a short result.Demo output: (not important)
Comparision to other solutions - (with respect to commit history of Python source repository):
This solution uses a private attribute of TestCase instance like many other solutions, but I checked carefully all relevant commits in the Python source repository that three alternative names cover the code history since Python 2.7 to 3.6.2 without any gap. It can be a problem after some new major Python release, but it could be clearly recognized, skipped and easily fixed later for a new Python. An advantage is that nothing is modified before running tearDown, it should never break the test and all functionality of unittest is supported, works with pytest and it could work many extending packages, but not with nosetest (not a suprise becase nosetest is not compatible e.g. with unittest.expectedFailure).
The solutions with decorators on the user test methods or with a customized failureException (mgilson, Pavel Repin 2nd way, kenorb) are robust against future Python versions, but if everything should work completely, they would grow like a snow ball with more supported exceptions and more replicated internals of unittest. The decorated functions have less readable tracebacks (even more levels added by one decorator), they are more complicated for debugging and it is unpleassant if another more important decorator has a problem. (Thanks to mgilson the basic functionality is ready and known issues can be fixed.)
The solution with modifired
run
method and catchedresult
parameterresult
is updated after tearDown call, never before.solution by
exc_info()
(Pavel Repin 2st way) works only with Python 2.Other solutions are principially similar, but less complete or with more disadvantages.
Explained by Python source repository
= Lib/unittest/case.py =
Python v 2.7 - 3.3
Python v. 3.4 - 3.6
Note (by reading Python commit messages): A reason why test results are so much decoupled from tests is memory leaks prevention. Every exception info can access to frames of the failed process state including all local variables. If a frame is assigned to a local variable in a code block that could also fail, then a cross memory refence could be easily created. It is not terrible, thanks to garbage collector, but the free memory can became fragmented more quickly than if the memory would be released correctly. This is a reason why exception information and traceback are converted very soon to strings and why temporary objects like
self._outcome
are encapsulated and are set to None in afinally
block in order to memory leaks are prevented.