Search code examples
pythonunit-testingnoseparameterized

How to dynamically create parameterized test from the file (json or yaml) in python


Really need an advice how to dynamically create parameterized tests from the file (json or yaml) in python. File contains list of urls

file example :

 { "name":"url_1", "url":"http://check.com","expected": "23"}

or :

{ "url_1", "http://check.com","23"}

Example like this:

@parameterized(from the file import name, expected)

def testUrl (self, name, url, expected):
    run something (%s) url
    assertTrue( expected = result)

OUTPUT :

test_for_url_1_ (test.TestClass) ... ok
test_for_url_2_ (test.TestClass) ... ok

I'm checking nose-parameterized for:

# An iterable of params
@parameterized(
    param.explicit(*json.loads(line))
    for line in open("testcases.jsons")
)
def test_from_json_file(...):
    ...

But can't make it work :(

Thank you in advance


Solution

  • In case you just want to pass params from file to single function:

    Yes, you can use decorator, pass filename to it, load json from file and use as decorated func's params:

    import json
    from functools import wraps
    
    
    def params_from_file(file):
        """Decorator to load params from json file."""
        def decorator(func_to_decorate):
            @wraps(func_to_decorate)
            def wrapper(self, *args, **kwargs):
                with open(file, 'r') as fh:
                    kwargs = json.loads(fh.read())
                    return func_to_decorate(self, **kwargs)
            return wrapper
        return decorator
    

    Usage:

    import unittest
    
    
    class Test(unittest.TestCase):
        @params_from_file('file.txt')  # {"name":"url_1", "url":"http://check.com", "expected": "23"}
        def test_url(self, name, url, expected):        
            self.assertEqual(name, 'url_1')
            self.assertEqual(url, 'http://check.com')
            self.assertEqual(expected, '23')
            # print(name, url, expected)
    
    
    if __name__ == "__main__":
        unittest.main()
    

    In case you want to create multiple new tests from params set:

    import json
    
    
    def create_tests(func_name, file):
        def decorator(cls):
            func = getattr(cls, func_name)
            # Set new funcs to class:
            with open(file, 'r') as fh:
                data = json.loads(fh.read())['data']
                for i, params in enumerate(data):
                    def tmp(params=params):  # need for http://stackoverflow.com/q/7546285/1113207
                        def wrapper(self, *args, **kwargs):
                            return func(self, **params)      
                        return wrapper          
                    setattr(cls, func_name + '_' + str(i), tmp())
            # Remove func from class:
            setattr(cls, func_name, None)
            return cls
        return decorator
    

    Usage:

    import unittest
    
    
    @create_tests('test_url', 'file.txt')
    class Test(unittest.TestCase):
        def test_url(self, name, url, expected):
            print(name, url, expected)
    
    
    if __name__ == "__main__":
        unittest.main(verbosity=2)
    

    Output:

    test_url_0 (__main__.Test) ... name1 https://example.com 175
    ok
    test_url_1 (__main__.Test) ... name2 https://example2.com 15
    ok
    
    ----------------------------------------------------------------------
    Ran 2 tests in 0.000s