Python unittest TestCase with inheritance

Currently, I have many similar tests for testing. Each TestCase contains both data (input values ​​+ expected output values) and logic (call SUT and compare the actual output with the expected output).

I would like to separate the data from the logic. So I want a base class that contains only logic and a derived class that contains only data. I came up with this so far:

import unittest class MyClass(): def __init__(self, input): self.input = input def get_result(self): return self.input * 2 class TestBase(unittest.TestCase): def check(self, input, expected_output): obj = self.class_under_test(input) actual_output = obj.get_result() self.assertEqual(actual_output, expected_output) def test_get_result(self): for value in self.values: self.check(value[0], value[1]) class TestMyClass(TestBase): def __init__(self, methodName='runTest'): unittest.TestCase.__init__(self, methodName) self.class_under_test = MyClass self.values = [(1, 2), (3, 6)] unittest.main(exit = False) 

But this fails with the following error:

 AttributeError: 'TestBase' object has no attribute 'values' 

Two questions:

  • Is my β€œdesign” good?
  • What else is needed to make it work?
+6
source share
4 answers

The design is (more or less) thin - one hiccup is that when unittest looks through all the TestCase classes and runs methods starting with a "test" on them. You currently have several options.

One approach is to specify the class under the test and the values ​​as attributes of the class. Here, if possible, you want the values ​​to be immutable ...

 class TestBase(unittest.TestCase): def check(self, input, expected_output): obj = self.class_under_test(input) actual_output = obj.get_result() self.assertEqual(actual_output, expected_output) def check_all(self): for value in self.values: self.check(value[0], value[1]) class TestMyClass1(TestBase): values = ((1, 2), (3, 4)) class_under_test = MyClass1 def test_it(self): self.check_all() class TestMyClass2(TestBase): values = (('a', 'b'), ('d', 'e')) class_under_test = MyClass2 def test_it(self): self.check_all() 
+3
source

To make this work expected, you minimally need:

  • Make sure that the init method of your test case of your subclass matches the TestCase test, i.e. __init__(self, methodName="runTest")
  • Add supercalls to the init method of your subclasses, for example. super(TestMyClass, self).__init__(methodName)
  • Add self argument for test_get_result, i.e. def test_get_result(self):

As for good design, remember that your tests partially act as documentation of how your code should work. If you have all the work hidden in the state of the TestCase instance, then what it does will not be so obvious. You might be better off, say, writing a mixin class that has custom statements that accept input and expected results.

+4
source

You double click test_get_result() . I do not think that any test*() methods should be in TestBase at all. Instead, use TestBase to provide custom statements, error TestBase , test data generators, etc. And save the actual tests in TestMyClass .

+2
source

Python unittest.main() runs all tests from all test classes in the current namespace.

One way not to run tests directly in the base class is to move it to another module (for example, move TestBase to testbase.py ):

In the file that unittest.main() calls, be sure not to import this class into the namespace (do not use from testbase import * or similar):

 import testbase class TestMyClass1(testbase.TestBase): values = ((1, 2), (3, 4)) class_under_test = MyClass1 
0
source

Source: https://habr.com/ru/post/982006/


All Articles