Hooked
Hooked

Reputation: 88198

Unittest from a dictionary of functions and values

I'm new to the unittests module in Python, so the answer may be obvious. I have a bunch of properties I'd like to check that I've stored in a dictionary:

petersen_prop = {
    "n_vertex" : 10,
    "n_edge"   : 15,
    ...
}

Each "key" is the name of a function and the value is the target I'd like unittests to verify. What works is the rather verbose code:

import unittest
class PetersenGraph(unittest.TestCase):

    def setUp(self):
        # Create the graph here (not important to the question)
        self.args = args
        self.adj = adj

    def test_n_vertex(self):
        self.assertTrue(n_vertex(self.adj,**self.args) == 
                        petersen_prop["n_vertex"])

    def test_n_edge(self):
        self.assertTrue(n_edge(self.adj,**self.args) == 
                        petersen_prop["n_edge"])

    # ..... hundreds of similar looking code blocks


unittest.main(verbosity=2)

It seems like I'm violating DRY, I have to copy and paste the same code block over and over. Is there a way to programmatically add these code blocks so unit tests will work as expected? To be clear, I'd like the input to be the dictionary above, and the output to be a unittest object that works identically.

Upvotes: 0

Views: 4306

Answers (2)

dano
dano

Reputation: 94911

You can iterate over the keys in the petersen_prop dictionary, and call the correct function based on the key name.

def test_n_props(self):
    for cur_prop in petersen_prop:
        func = globals()[cur_prop]
        self.assertTrue(func(self.adj,**self.args) == 
                        petersen_prop[cur_prop])

Or, if you don't import all the function names into the test module's namespace, like this:

def test_n_props(self):
    for cur_prop in petersen_prop:
        func = getattr(myfuncs, cur_prop)
        self.assertTrue(func(self.adj,**self.args) == 
                        petersen_prop[cur_prop])

You could also store the actual function object in the petersen_prop dict:

petersen_prop = {
    "n_vertex" : (10, n_vertex)
    "n_edge"   : (15, n_edge)
    ...
}

def test_n_props(self):
    for cur_prop in petersen_prop:
        func = petersen_map[cur_prop][1]
        self.assertTrue(func(self.adj,**self.args) == 
                        petersen_prop[cur_prop][0])

Edit:

Here's a way to add a unique test for each item in the dict, using a metaclass:

class TestMaker(type):
    def __new__(cls, clsname, bases, dct):
        # Add a method to the class' __dict__ for every key in
        # the petersen_prop dict.
        for prop in petersen_prop:
            dct['test_{}'.format(prop)] = cls.make_test(prop)

        return super(TestMaker, cls).__new__(cls, clsname, bases, dct)

    @staticmethod
    def make_test(prop):
        # test_wrap is the method body that will be used for each
        # test
        def test_wrap(self):
            func = getattr(myfuncs, prop)
            self.assertTrue(func(self.adj, **self.args) ==
                    petersen_prop[prop])
        return test_wrap

class PetersenGraph(unittest.TestCase):
   __metaclass__ = TestMaker

When you run this, you'll get a separate test case for each item. For example, in my test code I had each test fail:

======================================================================
FAIL: test_n_edge (__main__.PetersenGraph)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "un.py", line 26, in test_wrap
    petersen_prop[prop])
AssertionError: False is not true

======================================================================
FAIL: test_n_vertex (__main__.PetersenGraph)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "un.py", line 26, in test_wrap
    petersen_prop[prop])
AssertionError: False is not true

Upvotes: 1

user2867522
user2867522

Reputation:

Python doesn't have the functionality to get the name of the function from within the function. What you can do is test_n_vertex.__name__ and it will give you 'test_n_vertex' as a string. You can use basic string functions to do what you need to do. But the exact functionality you're expecting doesn't exist.

See here for more info Determine function name from within that function (without using traceback)

Upvotes: 0

Related Questions