0

In python 3.4, I want to be able to do a very simple dispatch table for testing purposes. The idea is to have a dictionary with the key being a string of the name of the function to be tested and the data item being the name of the test function.

For example:

myTestList = (
    "myDrawFromTo",
    "myDrawLineDir"
)

myTestDict = {
    "myDrawFromTo": test_myDrawFromTo,
    "myDrawLineDir": test_myDrawLineDir
}

for myTest in myTestList:
    result = myTestDict[myTest]()

The idea is that I have a list of function names someplace. In this example, I manually create a dictionary that maps those names to the names of test functions. The test function names are a simple extension of the function name. I'd like to compute the entire dictionary from the list of function names (here it is myTestList).

Alternately, if I can do the same thing without the dictionary, that'd be fine, too. I tried just building a new string from the entries in myTestList and then using local() to set up the call, but didn't have any luck. The dictionary idea came from the Python 3.x documentation.

martineau
  • 119,623
  • 25
  • 170
  • 301
PDX Mark
  • 31
  • 5

2 Answers2

2

There are two parts to the problem.

The easy part is just prefixing 'text_' onto each string:

tests = {test: 'test_'+test for test in myTestDict}

The harder part is actually looking up the functions by name. That kind of thing is usually a bad idea, but you've hit on one of the cases (generating tests) where it often makes sense. You can do this by looking them up in your module's global dictionary, like this:

tests = {test: globals()['test_'+test] for test in myTestList}

There are variations on the same idea if the tests live somewhere other than the module's global scope. For example, it might be a good idea to make them all methods of a class, in which case you'd do:

tester = TestClass()
tests = {test: getattr(tester, 'test_'+test) for test in myTestList}

(Although more likely that code would be inside TestClass, so it would be using self rather than tester.)


If you don't actually need the dict, of course, you can change the comprehension to an explicit for statement:

for test in myTestList:
    globals()['test_'+test]()

One more thing: Before reinventing the wheel, have you looked at the testing frameworks built into the stdlib, or available on PyPI?

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • @abamert: thank you, I think that I can make that work. I'm actually teaching an undergraduate CS service course for primarily non-CS students (but definitely students with NO programming experience) and the suggestion has been made that we use the turtle module. I'd like the students to build simple, but meaningful, functions and I'd like to stress writing tests at the same time. This would be a very simple way of introducing very simple test ideas without having to dive in to a bunch of other topics. Only about 30% of the students usually go on to enter the CS program. – PDX Mark Sep 12 '14 at 01:40
  • 1
    @PDXMark: I think that makes using a unit testing framework even more attractive, because then they don't have to learn about things like `globals` or `getattr` (or, worse, `eval`). They just define tests, and they magically run. A good framework will also make it easier to write tests that they might otherwise not know how to write (or might just not want to spend the time to write), like verifying that a function raises an exception, or printing out how the actual results different from the expected results. – abarnert Sep 12 '14 at 04:18
0

Abarnert's answer seems to be useful but to answer your original question of how to call all test functions for a list of function names:

def test_f():
    print("testing f...")

def test_g():
    print("testing g...")

myTestList = ['f', 'g']

for funcname in myTestList:
    eval('test_' + funcname + '()')
5gon12eder
  • 24,280
  • 5
  • 45
  • 92
  • 5gon12eder: works perfect. That will probably be easier for my audience. – PDX Mark Sep 12 '14 at 01:44
  • 1
    If you don't need the dict for anything, it makes sense tojust loop over the functions explicitly like this… but using `eval` instead of `globals` is a bad idea. – abarnert Sep 12 '14 at 04:15
  • as noted, using ```eval``` is a security risk. also see https://stackoverflow.com/questions/1933451/why-should-exec-and-eval-be-avoided – Ruperto Dec 28 '20 at 00:18