0

I have several test cases which all have a similar tearDown:

def tearDown(self):
    execution_time = time.time() - self.startTime
    result_list = [self._testMethodName]
    result = [str(x) for x in sys.exc_info()]
    if result[0] == 'None':
        result_list.append('PASS')
    elif 'Assertion' in result[0]:
        result_list.append('FAIL')
    else:
        result_list.append('ERROR')
    result_list.append(result)
    result_list.append(str(execution_time))
    TEST_RESULTS.append(result_list)

I'm using the tearDown function to store results from each test (in the test case) to a global TEST_RESULTS object (so each TestCase file has a TEST_RESULTS global defined).

Then in the tearDownClass function im doing this to store results to csv:

@classmethod
def tearDownClass(cls):
    with open ('tests/results/test_case_1_output.csv', 'wr') as resultFile:
        wr = csv.writer(resultFile)
        wr.writerows(TEST_RESULTS)

To me this is a terrible implementation. Globals defined everywhere and tearDown/tearDownClass implemented over and over again in each test case rather than defined once.

Furthermore, I would like to create a test result file which collects results from all test cases.

My hunch is this requires defining the file handle at the runner level (or somewhere before the TestCases are being called). This would allow me to reinitialize the csv file at a higher level (rather than arbitrarily in one TestCase).

Does anyone have a suggestion on how this can be accomplished? Did not see a way to do this from the docs and overriding django TestCase seemed hazardous.

DannyMoshe
  • 6,023
  • 4
  • 31
  • 53
  • 1
    You can create a base class where you define these functions once and inherit this class for your test cases. – xyres Jan 25 '19 at 09:21
  • So i did exactly this but still there are some issues. As far as i can tell there is only 1 unittest method which runs before a TestCase - [setUpClass](https://docs.python.org/3/library/unittest.html#setupclass-and-teardownclass). This method is not mutable so you cant override it in a base class. This means that if i want to "do something" before a test case runs i have to implement it in every TestCase. – DannyMoshe Jan 25 '19 at 17:24
  • A very hacky way i found was to create a test_method which runs before all other test_methods (they are run in alphabetical order) and have this method call the base class. – DannyMoshe Jan 25 '19 at 17:26
  • I don't think this method is not mutable. In fact, the documentation which you linked mentions this - *`"If you want the setUpClass and tearDownClass on base classes called then you must call up to them yourself."`* Use the `super` function to call base class's method. See this answer for reference code: https://stackoverflow.com/a/14044585/1925257 – xyres Jan 25 '19 at 18:40
  • You cant pass 'self' into the class (it only accepts a 'cls' argument), therefore it is not mutable (you need to pass in 'self' to call the superclass). The docs are simply saying you can implement those functions in your subclass as they are not implemented in the base (i believe they would be called abstract int the base class). – DannyMoshe Jan 25 '19 at 20:37
  • You cant pass 'self' into the **method** – DannyMoshe Jan 25 '19 at 21:24

1 Answers1

0

I will post my solution (thanks very much to @xyres) since i think it might help some others.

Below is an example of a TestCase which calls SetUp, tearDown and setUpClass from the base class (either TestManager or TestCase. The trick was to call setUpClass from base class 'TestCase' and create another initialization function 'initialize' called on the 'TestManager' base class.

class MyTestCase(TestManager, TestCase)
    def setUp(self):
        self.param1, self.param2 = super(MyTestCase, self).setUp()

    def tearDown(self):
       test_name = self._testMethodName
       super(MyTestCase, self).get_and_write_results_to_csv(test_name)

    @classmethod
    def setUpClass(cls):
        super(MyTestCase, cls).setUpClass()
        super(MyTestCase, cls).initialize('my test case name')


class TestManager():

    @classmethod
    def initialize(cls, test_case_name):
        with open('path/to/my/testresults.csv', 'wr') as resultFile:
            wr = csv.writer(resultFile)
            wr.writerow("Results for " + test_case_name + "are below:")

    def setUp(self):
        """
        Do some setup stuff that's the same for each TestCase.
        Im not showing the actions here but assume you want this 
        function to return 2 params that are the same for every 
        TestCase setup
        """
        return param1, param2

    def get_and_write_results_to_csv(self, test_name):
        execution_time = time.time() - self.startTime
        result_list = [test_name]
        result = [str(x) for x in sys.exc_info()]
        if result[0] == 'None':
            result_list.append('PASS')
        elif 'Assertion' in result[0]:
            result_list.append('FAIL')
        else:
            result_list.append('ERROR')
        result_list.append(result)
        result_list.append(str(execution_time))

        with open('path/to/my/testresults.csv', 'a') as resultFile:
            wr = csv.writer(resultFile)
            wr.writerow(result_list)
DannyMoshe
  • 6,023
  • 4
  • 31
  • 53