简体   繁体   中英

Randomly select functions

I am writing a test script which contains different functions for different tests. I would like to be able to randomly select a test to run. I have already achieved this with the following function...

test_options = ("AOI", "RMODE")

def random_test(test_options, control_obj):
  ran_test_opt = choice(test_options)

  if ran_test_opt.upper() == "AOI":
     logging.debug("Random AOI Test selected")
     random_aoi()
  elif ran_test_opt.upper() == "RMODE":
    logging.debug("Random Read Mode Test selected")
    random_read_mode(control_obj)

However, I want to be to add further test functions without having to modify the random test select function. All I would like to do is add in the test function to the script. Additionally I would also like a way to selecting which test will be included in the random selection. This is what the variable test_options does. How would I go about changing my random generate function to achieve this?

EDIT: I got around the fact that all the tests might need different arguments by including them all in a test class. All the arguments will be passed into the init and the test functions will refer to them using "self." when they need a specific variable...

class Test(object):
  """A class that contains and keeps track of the tests and the different modes"""

  def __init__(self, parser, control_obj):
    self.parser = parser
    self.control_obj = control_obj

  def random_test(self):
    test_options = []
    for name in self.parser.options('Test_Selection'):
        if self.parser.getboolean('Test_Selection', name):
            test_options.append(name.lower())

    ran_test_opt = choice(test_options)
    ran_test_func = getattr(self, ran_test_opt)
    ran_test_func()

  #### TESTS ####
  def random_aoi(self):
    logging.info("Random AOI Test")
    self.control_obj.random_readout_size()

  def random_read_mode(self):
    logging.info("Random Readout Mode Test")
    self.control_obj.random_read_mode()

You can create a list of functions in python which you can call:

test_options = (random_aoi, random_read_mode)

def random_test(test_options, control_obj):
    ran_test_opt = choice(test_options)
    ran_test_opt(control_obj) # call the randomly selected function

You have to make each function take the same arguments this way, so you can call them all the same way.

If you need to have some human readable names for the functions, you can store them in a dictionary, together with the function. I expect that you pass the control_obj to every function.

EDIT: This seems to be identical to the answer by @Ikke, but uses the dictionary instead of a list of functions.

>>> test_options = {'AOI': random_aoi, 'RMODE': random_read_mode}

>>> def random_test(test_options, control_obj):
...    ran_test_opt = test_options[choice(test_options.keys())]
...    ran_test_opt(control_obj)

Or you could pick out a test from test_options.values() . The 'extra' call to list() is because in python 3.x, dict.values() returns an iterator.

>>> ran_test_opt = choice(list(test_options.values()))

I'm gonna go out on a limb here and suggest that you actually use a real unittest framework for this. Python provides one -- conveniently name unittest . To randomly run a test, it would work like this:

import unittest

class Tests(unittest.TestCase):
    def test_something(self):
        ...
    def test_something_else(self):
        ...


if __name__ == '__main__':
    tests = list(unittest.defaultTestLoader.loadTestsFromTestCase(Tests))
    import random
    # repeat as many times as you would like...
    test = random.choice(tests)
    print(test)
    test()

To run all the tests, instead you would use: unittest.main() -- I suppose you could toggle which happens via a simple commandline switch.

This has the huge advantage that you don't need to keep an up-to-date list of tests separate from the tests themselves. If you want to add a new test, just add one and unittest will find it (as long as the method name starts with test ). It will also tell you information about which test runs and which one fails, etc.

If you wrapped your test selecting function and the tests themselves in a class, you could do the following:

from random import choice

class Test(object):
    """ Randomly selects a test from the methods with 'random' in the name """

    def random_aoi(self):
        print 'aoi'

    def random_read_mode(self):
        print 'read_mode'

    def random_text(self):
        print 'test'

    # add as many tests as needed here

    # important that this function doesn't have 'random' in the name(in my code anyway)
    def run_test(self): # you can add control_obj to the args 
        methods = [m for m in dir(self) if callable(getattr(self, m)) and 'random' in m]
        test = choice(methods)
        getattr(self, test)() # you would add control_obj between these parens also


app = Test()
app.run_test()

This makes it easy to add tests without the need to change any other code. Here is info on getattr

In addition to the other options, look at functools.partial . It allows to create closures for functions:

from functools import partial

def test_function_a(x,y):
   return x+y

def other_function(b, c=5, d=4):
   return (b*c)**d

def my_test_functions(some_input):
   funclist = (partial(test_function_a, x=some_input, y=4),
               partial(other_function, b=some_input, d=9))
   return funclist

random.choice(funclist)()

This lets you normalize the argument list for each test function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM