Last active
June 10, 2016 10:57
-
-
Save mfazekas/1710455 to your computer and use it in GitHub Desktop.
Parametric tests for nose/python unittest
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
""" Implement parametric testMethods and testCases for nose and unittest | |
usage for parametric test methods: | |
@parametric | |
class MyTest(unittest.TestCase): | |
@parametric(foo=[1,2],bar=[3,4]) | |
def testWithParams(self,foo,bar): | |
self.assertLess(foo,bar) | |
def testNormal(self): | |
self.assertEqual('foo','foo') | |
@parametric(foo=[1,2],bar=[3,4]) | |
class MyTest(unittest.TestCase): | |
def testWithParams(self,foo,bar): | |
self.assertLess(foo,bar) | |
def testWithParams2(self,foo,bar): | |
self.assertLess(-bar,-foo) | |
License: Copyright (c) 2008 Miklos Fazekas http://opensource.org/licenses/MIT | |
""" | |
import unittest | |
import inspect | |
import os | |
import re | |
def _gen_params(basename,params): | |
''' returns all variations of parameters and basename ''' | |
params = params.copy() | |
if len(params.keys()) > 0: | |
k = params.keys()[0] | |
vars = params.pop(k) | |
for vi in vars: | |
for (n,p) in _gen_params(basename,params): | |
p[k] = vi | |
yield (n+'_%s_%s'%(k,vi),p) | |
else: | |
yield (basename,{}) | |
class GenParamsTestCase(unittest.TestCase): | |
def testAllVariations(self): | |
s = list(_gen_params('foo',{'p1':[3,4],'p2':[1,2]})) | |
rows = [ ('foo_p1_3_p2_1',{'p1':3,'p2':1}), | |
('foo_p1_3_p2_2',{'p1':3,'p2':2}), | |
('foo_p1_4_p2_1',{'p1':4,'p2':1}), | |
('foo_p1_4_p2_2',{'p1':4,'p2':2})] | |
rows.sort() | |
s.sort() | |
self.assertEqual(rows,s) | |
class ParametricMethod(object): | |
''' this class is used internally for representing unexpanded parameteric calls ''' | |
def __init__(self,fun,params): | |
super(ParametricMethod,self).__init__() | |
self.fun = fun | |
self.params = params | |
def _gen_func(self,k,v): | |
def f(*args): | |
self.fun(*args,**v) | |
f.__name__=k | |
return f | |
def methods(self,basename): | |
for (k,v) in _gen_params(basename,self.params): | |
yield (k,self._gen_func(k,v)) | |
def _set_all_methods_to_parametric(original_class,kwargs): | |
def is_test_name(name): | |
return _testMatch.match(name) | |
def is_test_method(name,value): | |
return inspect.isroutine(value) and callable(value) and is_test_name(name) | |
tomod = [] | |
for k in original_class.__dict__: | |
v = original_class.__dict__[k] | |
if is_test_method(k,v): | |
tomod.append((k,ParametricMethod(v,kwargs))) | |
for (k,v) in tomod: | |
setattr(original_class,k,v) | |
def setattr_impl(k,v): | |
setattr(original_class,k,v) | |
_expand_parametric_methods(original_class.__dict__,setattr_impl) | |
return original_class | |
def _expand_parametric_methods(class_dict,setattr): | |
""" This function expand all parametric methods into actual methods """ | |
def is_param_method(name,value): | |
return isinstance(value,ParametricMethod) | |
tomod = [] | |
for k in class_dict: | |
v = class_dict[k] | |
if is_param_method(k,v): | |
tomod.append((k,v)) | |
for (k,v) in tomod: | |
for (m,f) in v.methods(k): | |
setattr(m,f) | |
def _parametric_func(func,kwargs): | |
return ParametricMethod(func,kwargs) | |
def _parametric_klass(klass,kwargs): | |
return _set_all_methods_to_parametric(klass,kwargs) | |
class ParametricTestCaseMetaClass(type): | |
def __new__(meta, classname, bases, classDict): | |
def setattr_impl(k,v): | |
classDict[k]=v | |
_expand_parametric_methods(classDict,setattr_impl) | |
res = type.__new__(meta, classname, bases, classDict) | |
return res | |
def parametric(*args,**kwargs): | |
if len(args) > 0: | |
if len(args) > 1: | |
raise ValueError("No more than 1 argument is expected for @parametric!") | |
if not inspect.isclass(args[0]): | |
raise ValueError("@parametric without args can be applied only to classes!") | |
orig_class = args[0] | |
def setattr_impl(k,v): | |
setattr(orig_class,k,v) | |
_expand_parametric_methods(orig_class.__dict__,setattr_impl) | |
return orig_class | |
def parametric_func_or_klass(func_or_klass): | |
if inspect.isclass(func_or_klass): | |
return _parametric_klass(func_or_klass,kwargs) | |
else: | |
return _parametric_func(func_or_klass,kwargs) | |
return parametric_func_or_klass | |
_testMatchPat = os.environ.get('NOSE_TESTMATCH', | |
r'(?:^|[\b_\.%s-])[Tt]est' % os.sep) | |
_testMatch = re.compile(_testMatchPat) | |
class ParametricTestCaseBase(object): | |
__metaclass__ = ParametricTestCaseMetaClass | |
@parametric(p1=[4,3]) | |
class SubTest(unittest.TestCase): | |
def testWithParams(self,p1): | |
self.assertEqual(p1,p1) | |
class SubTest2(unittest.TestCase,ParametricTestCaseBase): | |
def testFoo(self): | |
self.assertEqual(2,2) | |
@parametric(p1=[4,5]) | |
def testWithParams(self,p1): | |
self.assertEqual(p1,p1) | |
@parametric | |
class SubTest3(unittest.TestCase): | |
def testFoo(self): | |
self.assertEqual(2,2) | |
@parametric(p1=[4,5]) | |
def testWithParams(self,p1): | |
self.assertEqual(p1+1,p1+1) | |
if __name__=="__main__": | |
unittest.main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment