12

I am developing multiple functions that answer a same problem but using different algorithm.

So the same input for all functions should generate the same output, that's why I wnted to use the same unit tests instead of having to create multiple tests with the same logic.

I was using the Python unittest framework, and I wanted to use an abstract test class to have the generic tests defined with a function variable so that I could just instantiate that generic function with the one I want to test in another normal test class. But it seems I can't instantiate the function variable in the child class.

So here is an example abstract class with generic tests for multiple functions.

class AbstractTestCase():

    def test_generic_input_one(self):
        result = self.function("input 1")
        self.assertFalse(result)

    def test_generic_input_two(self):
        result = self.function("input 2")
        self.assertTrue(result)

And here you would have a specific test class for the function_a that inherits the generic tests from the AbstractTestCase class and that implements its own.

class TestsFunctionA(AbstractTestCase, unittest.TestCase):

    def setUp(self):
        self.function = function_a

    def test_specific_input(self):
        result = self.assertTrue(self.function("specific input"))
        self.assertTrue(result)

I am pretty sure it can be done, but I can't seem to find an example to see how to implement it. I would like to avoid code duplication.

What should be the simplest and best way to do it ?

2
  • 1
    Could you elaborate on this statement a bit more: "I wanted to use an abstract test class but it does not accept any kind of variable, so I can't pass the function."? Commented Jan 12, 2018 at 22:13
  • I have edited the question, my goal was to have the generic tests defined with a function variable in the abstract class so that I could just instantiate that generic function with the one I want to test in a child test class. But it seems I can't instantiate the function variable in the child class. Commented Jan 12, 2018 at 22:26

4 Answers 4

12

I have been looking for it and got a couple of example like:

But what helped me the most was vegard's answer about making a class factory which would take parameters and create the TestCase accordingly

The function takes the parameters of the parameterised test case and the actual TestCase class can refer to them without any problems.

Here is an example, take a foo.py file with:

import unittest

def make_test_case(x):
    class MyTestCase(unittest.TestCase):
        def test_foo(self):
            self.assertEquals(x, 1)

    return MyTestCase

class ConcreteTestCase(make_test_case(1)): 
    pass

Then run the test(s):

python -m unittest -v foo

Basically this is very flexible and adapted really well to my usecase.

Sign up to request clarification or add additional context in comments.

1 Comment

This will work even if you're using pytest. You could just leave out the inheritance.
3

Basically you need to parametrized you tests with function.

For unittest you can use ddt

@ddt
class ProblemTestCase(unittest.TestCase):
    def test_specific_input(self):
        self.assertTrue(function_a("specific input"))

    @data(function_a, function_b)
    def test_generic_input_one(self, function):
        result = function("input 1")
        self.assertFalse(result)

    @data(function_a, function_b)
    def test_generic_input_two(self, function):
        result = function("input 2")
        self.assertTrue(result)

Alternatively you can use just plain OOP:

class AbstractTestCase(object):

    def test_generic_input_one(self):
        result = self.function("input 1")
        self.assertFalse(result)

    def test_generic_input_two(self):
        result = self.function("input 2")
        self.assertTrue(result)

class TestsFunctionA(AbstractTestCase, unittest.TestCase):

    def function(self, param):
        return function_a(param)

    def test_specific_input(self):
        self.assertTrue(self.function("specific input"))

class TestsFunctionB(AbstractTestCase, unittest.TestCase):

    def function(self, param):
        return function_b(param)

    def test_another_specific_input(self):
        self.assertTrue(self.function("another specific input"))

3 Comments

Thanks you for your answer! --- I think ddt is deprecated but pytest seems to have the same feature with annotation. For the plain OOP example: it's very nice! I was wondering if it would handle multiple inheritance. I am going to check that with your example. What do you think of the answer I found?
Multiple inheritance will work fine. IMHO the solution with class generation is unnecessarily complex.
Oh there's something that I have found out, you can't have unittest.Testcase inherited to the AbstractTestClass otherwise the tests would run in it and fail. It should be something like TestFunctionA(AbstractTestCase, unittest.TestCase) instead to have the tests run with the sub class.
2

I came here searching for a way to test multiple implementations of the same function. My use case is testing student's submissions of different search algorithms that are all fed the same test data and should return the same results.

Sylhare's answer was easy to adopt but it does not hurt to share so here is how:

import unittest

def function_a():
    result = ...
    return result

def function_b():
    result = ...
    return result

def make_test(function):

    class TestImplementation(unittest.TestCase):

        def test_foo(self):
            self.assertEquals(function, 1)

    return TestImplementation

class TestImplementationA(make_test(function_a)): 
    pass

class TestImplementationB(make_test(function_b)): 
    pass

Comments

2

Given a structure

├── README.md
├── requirements.txt
├── test
│   ├── __init__.py
│   └── two_sum
│       ├── __init__.py
│       ├── base_test_suite.py
│       ├── test_brute_force.py
│       └── test_two_pass_hash_table.py
└── two_sum
    ├── __init__.py
    ├── brute_force.py
    └── two_pass_hash_table.py

And there are brute-force and two-pass hash-table solutions (functions called two_sum) in the corresponding files in two_sum module.

And base_test_suite.py

class TestTwoSum:
    def __init__(self, unittest, two_sum_func):
        self.two_sum = two_sum_func
        self.unittest = unittest

    def test_it_returns_indices_of_two_numbers_that_add_up_to_target(self):
        # given
        numbers = [2, 7, 11, 15]
        target = 9

        # when
        result = self.two_sum(numbers, target)

        # then
        self.unittest.assertEqual(result, [0, 1])

And test_brute_force.py

from unittest import TestCase
from test.two_sum.base_test_suite import TestTwoSum
from two_sum.brute_force import two_sum


class Test(TestCase):
    def test(self):
        case = TestTwoSum(self, two_sum)
        case.test_it_returns_indices_of_two_numbers_that_add_up_to_target()

And test_two_pass_hash_table.py

from unittest import TestCase
from test.two_sum.base_test_suite import TestTwoSum
from two_sum.two_pass_hash_table import two_sum


class Test(TestCase):
    def test(self):
        case = TestTwoSum(self, two_sum)
        case.test_it_returns_indices_of_two_numbers_that_add_up_to_target()

Then one can run python -m unittest which would run the same unit test for different solutions of the two sum problem.

1 Comment

So, for every test case in TestTwoSum there are two more than call it with different implementations. That's not really maintainable.

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.