0

I'm trying to create a test case using the Unittest framework in python, where I can provide arguments to both setUp and test_case functions.

So far I've created a Fixture class, which inherits from TestCase, initialized a variable in the constructor which can be passed as an argument later on (self.option).

Later on in another class, I've used the super() function to initialize the __init__ function of the parent class, and I'm also providing an argument (self.option) that I'm hoping gets through to both setUp and test_case functions in the parent class.

Eventually, I'm just calling the inherited self.test_case() function, which also invokes the setUp function of the same class.

The code looks like this:

from unittest import TestCase

class Fixture(TestCase):
    def __init__(self, option):
        self.option = option
        super(Fixture, self).__init__(self.option)

    def setUp(self):
        print(f'\nSetting up option {self.option}')

    def test_case(self):
        print(f'Testing option {self.option}')

class Runner(Fixture):
    def __init__(self):
        self.option = 'one'
        super().__init__(self.option)

    def case(self):
        self.test_case()

The test passes and I get the following output:

Process finished with exit code 0
PASSED                                      [100%]
Setting up option test_case
Testing option test_case

However, in the printouts, I was expecting to see something like Setting up option one, but I'm getting Setting up option test_case instead - with function name as a received variable, as it seems.

What's wrong here? What would be the correct approach to solve this?

1 Answer 1

1

There are a few things to clear up here. What you are trying to do is normally done with with the setUpClass classmethod of TestCase. This is a working example:

from unittest import TestCase


class Fixture(TestCase):
    option = None

    def run_test(self):
        self.assertEqual('one', self.option)


class Runner(Fixture):
    @classmethod
    def setUpClass(cls):
        cls.option = 'one'

    def test_case(self):
        self.run_test()
  1. I've renamed your Fixture.test_case method to run_test and your Runner.case method to test_case. I assume you want to run the tests in the Runner classes. unittest determines which methods are tests by looking at whether their name starts with "test". So your code was actually running the test_case method of the parent class.
  2. The unexpected output of your code was because you were calling the __init__ of TestCase and you are actually passing self.option to it in this line: super(Fixture, self).__init__(self.option). unittest does some pretty convoluted stuff with the method names and somehow it set self.option to "test_case". I'm not sure exactly what happened there, but basically don't mess with the __init__ in unittest unless you really know what you are doing.

With this setup you can define more subclasses with different values in setUpClass and define more tests in the Fixture class (just make sure that their names don't start with test).

I'm not sure if these class names are appropriate. I believe actually the names of the classes should be reverted. The subclasses are doing the fixtures and the parent class is actually running the tests. So maybe something like this would be better:

from unittest import TestCase


class Runner(TestCase):
    option = None

    def run_test(self):
        self.assertEqual('one', self.option)


class Fixture_1(Runner):
    @classmethod
    def setUpClass(cls):
        cls.option = 'one'

    def test_case(self):
        self.run_test()


class Fixture_2(Runner):
    @classmethod
    def setUpClass(cls):
        cls.option = 'two'

    def test_case(self):
        self.run_test()
Sign up to request clarification or add additional context in comments.

1 Comment

thanks, this looks good. But there's still a problem that I cannot wrap my head around - the main goal of this effort is to avoid code duplication. While your solution defines the test case just once, and calls it twice (which is really cool), the problem is with the duplicating "setUpClass" each time. Can't it somehow be defined once in the Runner class as well, and just called in the Fixture class later?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.