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

…衆ロ難τιáo~ 提交于 2019-12-10 14:45:53

问题


Edit: solved!

Will update shortly with a solution.


Aim:

I want to rewrite Python's UnitTest module so when I call it I get the following JSON output within the stdout stream. For example:

{  
   "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've written some code to generate these test results, but I'm facing problems with writing code for the test_data attribute of the JSON array. I've overwritten the TestCase, TextTestResult and TextTestRunner classes but I can't seem to 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's 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 isn't the value of x updating after calling getTestsReport()?

Edit:

Okay, I think unittest.main(exit=False) was calling the wrong function! I'm now trying to rewrite the code in my main, but still facing a bunch of issues:

#!/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's 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 anybody help me out here? I'm so stuck :(

来源:https://stackoverflow.com/questions/29866182/overriding-python-unit-test-module-for-custom-output-code-updated

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!