Package: parametrize
Drop-in@pytest.mark.parametrizereplacement working withunittest.TestCaseWhy?You want to start using@pytest.mark.parametrize, but can't simply dropunittest.TestCasebecause you have tons ofself.assert's,setUp'stearDown's to rewrite?With@parametrizeyou can start parameterizing your tests now, and get rid ofunittest.TestCaselater if needed.UsageSimple example frompytest docsadapted to unittestimportunittestfromparametrizeimportparametrizeclassTestSomething(unittest.TestCase):@parametrize('test_input,expected',[("3+5",8),("2+4",6)])deftest_eval(self,test_input,expected):self.assertEqual(expected,eval(test_input))$python-munittesttest.py-vtest_eval[2+4-6](test.TestSomething)...oktest_eval[3+5-8](test.TestSomething)...oktest_eval[6*9-42](test.TestSomething)...FAIL======================================================================FAIL:test_eval[6*9-42](test.TestSomething)----------------------------------------------------------------------Traceback(mostrecentcalllast):File"parametrize/parametrize.py",line261,inparametrized_methodreturnparametrized_func(*args,**kwargs)File"test.py",line8,intest_evalself.assertEqual(expected,eval(test_input))AssertionError:42!=54----------------------------------------------------------------------Ran3testsin0.001sFAILED(failures=1)You don't need to use additional decorators, custom base classes or metaclasses.Stacking parametrize decorators:importunittestfromparametrizeimportparametrizeclassTestSomething(unittest.TestCase):@parametrize("x",[0,1])@parametrize("y",[2,3])deftest_foo(self,x,y):passtest_foowill be called with:(x=0, y=2),(x=1, y=2),(x=0, y=3), and(x=1, y=3):$python-munittesttest.py-vtest_foo[2-0](test.TestSomething)...oktest_foo[2-1](test.TestSomething)...oktest_foo[3-0](test.TestSomething)...oktest_foo[3-1](test.TestSomething)...ok----------------------------------------------------------------------Ran4testsin0.000sOKNote: even though the tests are always generated in the same order, the execution order is not guaranteedCompatibilityAny@parametrizedecorator can be converted to@pytest.mark.parametrizejust by changing its name.@pytest.mark.parametrizedecorator can be converted to@parametrizeas long aspytest.param,indirect,idsandscopeare not used.@parametrizeworks with bothunittestandpytest. However,pytestis recommended due tolimitations when using unittest in cli.Parametrized tests are properly detected and handled by PyCharm. They are displayed as if they were parametrized with@pytest.mark.parametrize.LimitationsSince@parametrizedoes some kind of magic under the hood, there are some limitations you need to consider.
It's likely you will never face most of them, but if you will,@parametrizewill let you know with an error:All parametrization must be done via decorators:white_check_mark: OK@parametrize('a',(1,2))deff(a):...:x: Won't work:deff(a):...parametrize('a',(1,2))(func)RuntimeError:Unabletofindanyparametrizesindecorators,pleaserewritedecoratornametomatchanyofdetectednames@{'parametrize'}All other decorators must be defined before parametrize decorators:white_check_mark: OK:@parametrize("a",(1,2))@parametrize("b",(2,3))@mock.patch(f"{__name__}.bar","foo")deff(a,b):returna,b:x: Won't work:@mock.patch(f"{__name__}.bar","foo")@parametrize("a",(1,2))@parametrize("b",(2,3))deff(a,b):returna,bTypeError:@mock.patch(f"{__name__}.bar","foo")mustbedefinedbeforeanyof@{'parametrize'}decoratorsIf you assign parametrized decorator to variable, it must be accessible fromlocals()orglobals()namespaces::white_check_mark: OK:a_parameters=parametrize("a",(4,5))# defined in moduledeffunc():classTestSomething:b_parameters=parametrize("b",(1,2,3))@b_parameters# b_parameters found in locals()@a_parameters# a_parameters found in globals()deftest_method(self,a,b):...:x: Won't work:deffunc():# defined in function scopea_parameters=parametrize("a",(4,5))classTestSomething:print('a_parameters'in{**globals(),**locals()})# False@a_parameters# accessed in class body scopedeftest_method(self,a,b):...RuntimeError:Unabletofindanyparametrizesindecorators,pleaserewritedecoratornametomatchanyofdetectednames@{'parametrize'}Parametrized method can be ran from command line only via pytest:$ cat test.pyimportunittestfromparametrizeimportparametrizeclassTestSomething(unittest.TestCase):@parametrize('a',(1,2))deftest_something(self,a):self.assertIsInstance(a,int):white_check_mark: OK:$ pytest test.py::TestSomething::test_something -v...test.py::TestSomething.test_something[1]✓50%█████test.py::TestSomething.test_something[2]✓100%██████████Results(0.07s):2passed:x: Won't work:$ python -m unittest test.TestSomething.test_somethingTraceback(mostrecentcalllast):...TypeError:don't know how to make test from: test_something[...]@parametrizecannot be used in interactive environments like REPL (It works in IPython though)@parametrizecannot be used in cythonized code