3

How do you call a function after each test in a Python unittest.TestCase derived class based on the test result?

For instance, lets say we have the following test class:

import sys
from unittest import TestCase

class TestFeedback(TestCase):
  def msg(self, text):
    sys.stdout.write(text + ' ...')

  def on_fail(self):
    sys.stdout.write(' FAILED!\n')

  def on_success(self):
    sys.stdout.write(' SUCCEEDED!\n')

  def test_something(self):
    self.msg('Testing whether True == 1')
    self.assertTrue(True == 1)

  def test_another(self):
    self.msg('Testing whether None == 0')
    self.assertEqual(None, 0)

I would like the methods on_success() or on_fail() to be called after each test depending on the outcome of the test, e.g.

>>> unittest.main()
...
Testing whether True == 1 ... SUCCEEDED!
Testing whether None == 0 ... FAILED!
<etc.>

Can this be done and, if so, how?

Liam Deacon
  • 904
  • 1
  • 9
  • 25
  • 1
    Related to what you are asking, you can pass an additional argument to each `assert` method that will be displayed if the test fails. In general, you might want to look at using a custom subclass of `unittest.result.TestResult` – chepner May 21 '15 at 17:02
  • Did the answer below solve your problem? – muppetjones May 27 '15 at 19:27

1 Answers1

2

As of right now, I don't think you can do this. The TestResult object is gone before you get to your tearDown method, which would most likely be the easiest.

Instead, you could roll your own TestSuite (see here for a basic explanation), which should give you access to the results for each test. The downside is that you would have to add each test individually or create your own discovery method.

Another option would be to pass an error message into your asserts; the messages will be printed on fail:

self.assertEqual(None, 0, 'None is not 0')

What is your end goal here? Running the unittests will tell you which tests failed with traceback information, so I imagine you have a different goal in mind.

Edit:
Alright, I think one solution would be to write your own custom TestCase class and override the __call__ method (note: I haven't tested this):

from unittest import TestCase    
class CustomTestCase(TestCase):
    def __call__(self, *args, **kwds):
        result = self.run(*args, **kwds)
        <do something with result>
        return result

Edit 2:
Another possible solution...check out this answer

Community
  • 1
  • 1
muppetjones
  • 296
  • 3
  • 14
  • Thanks for the good advice - your suspicions regarding an alternative goal are correct. I was hoping to have the possibility to call a method, say one that appends the result to a custom results table of successful and unsuccessful tests, however perhaps it may be easier for me to just produce a report using something like [HTMLTestRunner](http://tungwaiyip.info/software/HTMLTestRunner.html)... – Liam Deacon May 22 '15 at 07:25
  • I usually prefer to try an existing solution instead of reinventing the wheel, but if it's a simple case, maybe you could use a custom `TestCase` (see edit). – muppetjones May 22 '15 at 18:19
  • I have accepted your answer as I feel you have put a lot of thought and effort into your response - Thanks again! – Liam Deacon Jun 10 '15 at 08:35
  • I appreciate it! Did it actually solve your problem? – muppetjones Jun 10 '15 at 21:19
  • As it turns out implementing a custom `TestCase` was the simplest & most flexible (and therefore best IMHO) - it achieved what I wanted, but I did like the second edit pointing to 3rd party packages! – Liam Deacon Jun 11 '15 at 07:36