Overriding Python Unit Test module for user output? [updated code]

Edit : solved!

The solution will be updated soon.


Goal:

I want to rewrite the Python UnitTest module, so when I call it, I get the following JSON output in the stdout stream. For instance:

 { "errors":0, "failures":1, "ran":3, "skipped":0, "successful":2, "test_data":[ { "index":0, "result":1 }, { "index":1, "result":1 }, { "index":2, "result":-1 } ] } 

Problem:

I wrote code to generate these test results, but I have problems writing code for the test_data attribute of the JSON array. I have overwritten the TestCase , TextTestResult and TextTestRunner , but I cannot figure out how to get the result from getTestsReport() :

 #!/usr/bin/python import unittest import sys, os import json class dataHolder(object): x = None class MyTestRunner(unittest.TextTestRunner): def _makeResult(self): obj = MyTestResult(self.stream, self.descriptions, self.verbosity) dataHolder.x = obj.getTestsReport() return obj class MyTestResult(unittest._TextTestResult): """ Holder for test result information. Test results are automatically managed by the TestCase and TestSuite classes, and do not need to be explicitly manipulated by writers of tests. Each instance holds the total number of tests run, and collections of failures and errors that occurred among those test runs. The collections contain tuples of (testcase, exceptioninfo), where exceptioninfo is the formatted traceback of the error that occurred. """ _previousTestClass = None _testRunEntered = False _moduleSetUpFailed = False def __init__(self, stream=None, descriptions=None, verbosity=None): self.failfast = False self.failures = [] self.errors = [] self.testsRun = 0 self.skipped = [] self.expectedFailures = [] self.unexpectedSuccesses = [] self.shouldStop = False self.buffer = False self._stdout_buffer = None self._stderr_buffer = None self._original_stdout = sys.stdout self._original_stderr = sys.stderr self._mirrorOutput = False # List containing all the run tests, their index and their result. This is the new line of code. self.tests_run = [] def getTestsReport(self): """Returns the run tests as a list of the form [test_description, test_index, result]""" return self.tests_run ### ### Modified the functions so that we add the test case to the tests run list. ### -1 means Failure. 0 means error. 1 means success. ### def addError(self, test, err): """ Called when an error has occurred. 'err' is a tuple of values as returned by sys.exc_info(). """ self.errors.append((test, self._exc_info_to_string(err, test))) self._mirrorOutput = True self.tests_run.append([test.shortDescription(), self.testsRun, 0]) TestResult.addError(self, test, err) def addFailure(self, test, err): """ Called when an error has occurred. 'err' is a tuple of values as returned by sys.exc_info(). """ self.failures.append((test, self._exc_info_to_string(err, test))) self._mirrorOutput = True self.tests_run.append([test.shortDescription(), self.testsRun, -1]) TestResult.addFailure(self, test, err) def addSuccess(self, test): "Called when a test has completed successfully" self.tests_run.append([test.shortDescription(), self.testsRun, 1]) TestResult.addSuccess(self, test) class MyTest(unittest.TestCase): currentResult = None # holds last result object passed to run method results = [] # Holds all results so we can report back to the CCC backend @classmethod def setResult(cls, amount, errors, failures, skipped): cls.amount, cls.errors, cls.failures, cls.skipped = \ amount, errors, failures, skipped def tearDown(self): amount = self.currentResult.testsRun errors = self.currentResult.errors failures = self.currentResult.failures skipped = self.currentResult.skipped self.setResult(amount, errors, failures, skipped) @classmethod def tearDownClass(cls): print json.dumps( { 'ran': cls.amount, 'errors': len(cls.errors), 'failures': len(cls.failures), 'succeeded': cls.amount - len(cls.errors) - len(cls.failures), 'skipped': len(cls.skipped), 'test_data' : dataHolder.x }, sort_keys=True, indent=4, separators=(',', ': ') ) return def run(self, result=None): self.currentResult = result # remember result for use in tearDown unittest.TestCase.run(self, result) # call superclass run() method # Tests are defined below. def test_something(self): self.assertEqual(1, 2) if __name__ == '__main__': MyTestRunner( stream=None, descriptions=0, verbosity=0 ) unittest.main(exit=False) 

Why not update x after calling getTestsReport() ?

Edit

Well, I think unittest.main(exit=False) was calling the wrong function! I'm trying to rewrite the code in my main one, but still encountering a number of problems:

 #!/usr/bin/python import unittest import sys, os import json import string class MyTestRunner(unittest.TextTestRunner): def _makeResult(self): return MyTestResult(self.stream, self.descriptions, self.verbosity) class MyTestResult(unittest._TextTestResult): """ Holder for test result information. Test results are automatically managed by the TestCase and TestSuite classes, and do not need to be explicitly manipulated by writers of tests. Each instance holds the total number of tests run, and collections of failures and errors that occurred among those test runs. The collections contain tuples of (testcase, exceptioninfo), where exceptioninfo is the formatted traceback of the error that occurred. """ _previousTestClass = None _testRunEntered = False _moduleSetUpFailed = False def __init__(self, stream=None, descriptions=None, verbosity=None): self.failfast = False self.failures = [] self.errors = [] self.testsRun = 0 self.skipped = [] self.expectedFailures = [] self.unexpectedSuccesses = [] self.shouldStop = False self.buffer = False self._stdout_buffer = None self._stderr_buffer = None self._original_stdout = sys.stdout self._original_stderr = sys.stderr self._mirrorOutput = False # List containing all the run tests, their index and their result. This is the new line of code. self.tests_run = [] def getTestsReport(self): """Returns the run tests as a list of the form [test_description, test_index, result]""" return self.tests_run ### ### Modified the functions so that we add the test case to the tests run list. ### -1 means Failure. 0 means error. 1 means success. ### def addError(self, test, err): """ Called when an error has occurred. 'err' is a tuple of values as returned by sys.exc_info(). """ self.errors.append((test, self._exc_info_to_string(err, test))) self._mirrorOutput = True self.tests_run.append([test.shortDescription(), self.testsRun, 0]) TestResult.addError(self, test, err) def addFailure(self, test, err): """ Called when an error has occurred. 'err' is a tuple of values as returned by sys.exc_info(). """ self.failures.append((test, self._exc_info_to_string(err, test))) self._mirrorOutput = True self.tests_run.append([test.shortDescription(), self.testsRun, -1]) TestResult.addFailure(self, test, err) def addSuccess(self, test): "Called when a test has completed successfully" self.tests_run.append([test.shortDescription(), self.testsRun, 1]) TestResult.addSuccess(self, test) class MyTest(unittest.TestCase): currentResult = None # holds last result object passed to run method results = [] # Holds all results so we can report back to the CCC backend @classmethod def setResult(cls, amount, errors, failures, skipped): cls.amount, cls.errors, cls.failures, cls.skipped = \ amount, errors, failures, skipped def tearDown(self): amount = self.currentResult.testsRun errors = self.currentResult.errors failures = self.currentResult.failures skipped = self.currentResult.skipped self.setResult(amount, errors, failures, skipped) @classmethod def tearDownClass(cls): print json.dumps( { 'ran': cls.amount, 'errors': len(cls.errors), 'failures': len(cls.failures), 'succeeded': cls.amount - len(cls.errors) - len(cls.failures), 'skipped': len(cls.skipped), 'test_data' : dataHolder.x }, sort_keys=True, indent=4, separators=(',', ': ') ) return def run(self, result=None): self.currentResult = result # remember result for use in tearDown unittest.TestCase.run(self, result) # call superclass run() method # Tests are defined below. def test_something(self): self.assertEqual(1, 2) if __name__ == '__main__': module = __import__('__main__') testRunner = MyTestRunner(verbosity=0) test = unittest.defaultTestLoader.loadTestsFromModule(module) print test test2 = unittest.defaultTestLoader.loadTestsFromNames(test, module) result = unittest.testRunner.run(test2) 

Can anyone help me here? I'm so stuck :(

+5
source share

All Articles