Search code examples
pythonclassmetaprogrammingscopes

Python Metaprogramming. Generating massive amount of similar tests where a variable value is changed


We have a massive amount of test cases classes to perform some checks. I need to generate all those same tests but change the value of a variable. For example:

class DynamicPivotSeriesTestCase(AuthenticatedApiTestCase):
    '''
    Test all series against a fixed category trying every format
    '''
    #binding
    ftn = utils.format_test_name

    # Test Params
    base_url, formats = API_FORMATS['pivot']
    base_test_name = 'test_pivot_series_{series}'

    # Test vars
    series = VARS

    for srs in series:
        params = {'series': srs, 'cats': 'campaign', 'from': '2013-07-31', 'to': '2013-07-31'}
        test_name = ftn(base_test_name, params)
        locals()[test_name] = utils.make_check_code(200, base_url, params, formats)

class DynamicPivotDerivedSeriesTestCase(AuthenticatedApiTestCase):
    #binding
    ftn = utils.format_test_name

    # Test Params
    base_url, formats = API_FORMATS['pivot']
    base_test_name = 'test_pivot_derived_series_{series}'

    # Test vars
    series = DERIVED_VARS

    for srs in series:
        params = {'series': srs, 'cats': 'campaign', 'from': '2013-07-31', 'to': '2013-07-31'}
        test_name = ftn(base_test_name, params)
        locals()[test_name] = utils.make_check_code(200, base_url, params, formats)

There are like 150 tests like that, I can't copy paste the code. I need to iterate over globals, access to every (class_name, class object), check if the class is a test class, and if so I need to instantiate a new test class that has the same body as the class test currently in access, but I need to set a different value to base_url variable. This is what I don' t understand how to achieve.


Solution

  • simple update of object attribute:

    if __name__ == '__main__':
        for name, thing in globals().iteritems():
            if issubclass(thing, AuthenticatedApiTestCase):
                obj = thing()
                obj.base_url = something_new
    

    if you can't instantiate then you can do

    new_classes = []
    if issubclass(thing, AuthenticatedApiTestCase):
        class NewClasS(thing):
            base_url = something_new
    
        new_classes.append(NewClass)
    

    ok that's probably not exactly what you want - you'll want to dynamically assign the class name etc... but maybe this solves your initial problem of dynamically generating new classes with modified class vars

    there are other ways - class decorators, metaclasses - it really depends on other details about what you are trying to do