0

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.

dano
  • 91,354
  • 19
  • 222
  • 219
Hooked
  • 84,485
  • 43
  • 192
  • 261
  • Where are the `n_edge` and `n_vertex` functions defined? – dano Aug 12 '14 at 14:53
  • @dano They are local in scope, in that a call from the global block `n_vertex(xxx,**xxx)` would work. I do this by importing the functions via `from my_funcs import *`, bad practice, but I'm not sure the right way to get this to work. – Hooked Aug 12 '14 at 14:55

2 Answers2

1

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
dano
  • 91,354
  • 19
  • 222
  • 219
  • The second method works (and thanks!), but won't that only make a single test case rather than multiple test cases? Isn't the power of `unittests` to be able to have other programs determine what exactly failed in a new build? – Hooked Aug 12 '14 at 15:00
  • @Hooked I've edited my answer to include a solution that adds a separate test for each item in the dict. – dano Aug 12 '14 at 15:13
0

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)

Community
  • 1
  • 1
  • I'm not trying to resolve the name of the function (I think), I'm trying to create new methods in an existing class whose functions names I already know and have them added as new test cases. – Hooked Aug 12 '14 at 14:57
  • Gotcha. In that case you can use `eval` to convert string to the function names (`test_n_vertex` for example) or object names (`n_vertex` for example) –  Aug 12 '14 at 15:00
  • [Don't use `eval`](http://stackoverflow.com/questions/1832940/is-using-eval-in-python-a-bad-practice). Favor any of the first three options in my answer over that. – dano Aug 12 '14 at 15:17