You want to start using @pytest.mark.parametrize, but can't simply drop unittest.TestCase because you have tons of self.assert's, setUp's tearDown's to rewrite?
With @parametrize you can start parameterizing your tests now, and get rid of unittest.TestCase later if needed.
Simple example from pytest docs adapted to unittest
import unittest
from parametrize import parametrize
class TestSomething(unittest.TestCase):
@parametrize('test_input,expected', [("3+5", 8), ("2+4", 6), ("6*9", 42)])
def test_eval(self, test_input, expected):
self.assertEqual(expected, eval(test_input))$ python -m unittest test.py -v
test_eval[2+4-6] (test.TestSomething) ... ok
test_eval[3+5-8] (test.TestSomething) ... ok
test_eval[6*9-42] (test.TestSomething) ... FAIL
======================================================================
FAIL: test_eval[6*9-42] (test.TestSomething)
----------------------------------------------------------------------
Traceback (most recent call last):
File "parametrize/parametrize.py", line 261, in parametrized_method
return parametrized_func(*args, **kwargs)
File "test.py", line 8, in test_eval
self.assertEqual(expected, eval(test_input))
AssertionError: 42 != 54
----------------------------------------------------------------------
Ran 3 tests in 0.001s
FAILED (failures=1)import unittest
from parametrize import parametrize
class TestSomething(unittest.TestCase):
@parametrize("x", [0, 1])
@parametrize("y", [2, 3])
def test_foo(self, x, y):
passtest_foo will be called with: (x=0, y=2), (x=1, y=2), (x=0, y=3), and (x=1, y=3):
$ python -m unittest test.py -v
test_foo[2-0] (test.TestSomething) ... ok
test_foo[2-1] (test.TestSomething) ... ok
test_foo[3-0] (test.TestSomething) ... ok
test_foo[3-1] (test.TestSomething) ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OKNote: even though the tests are always generated in the same order, the execution order is not guaranteed
Any @parametrize decorator can be converted to @pytest.mark.parametrize just by changing its name.
@pytest.mark.parametrize decorator can be converted to @parametrize as long as pytest.param, indirect, ids and scope are not used.
@parametrize works with both unittest and pytest. However, pytest is recommended due to limitations 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.
Since @parametrize does 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, @parametrize will let you know with an error:
-
✅ OK
@parametrize('a', (1, 2)) def f(a): ...
❌ Won't work:
def f(a): ... parametrize('a', (1, 2))(f)
RuntimeError: Unable to find any parametrizes in decorators, please rewrite decorator name to match any of detected names @{'parametrize'}
-
✅ OK:
@parametrize("a", (1, 2)) @parametrize("b", (2, 3)) @mock.patch(f"{__name__}.bar", "foo") def f(a, b): return a, b
❌ Won't work:
@mock.patch(f"{__name__}.bar", "foo") @parametrize("a", (1, 2)) @parametrize("b", (2, 3)) def f(a, b): return a, b
TypeError: @mock.patch(f"{__name__}.bar", "foo") must be defined before any of @{'parametrize'} decorators
-
If you assign parametrized decorator to variable, it must be accessible from
locals()orglobals()namespaces:✅ OK:
a_parameters = parametrize("a", (4, 5)) # defined in module def func(): class TestSomething: b_parameters = parametrize("b", (1, 2, 3)) @b_parameters # b_parameters found in locals() @a_parameters # a_parameters found in globals() def test_method(self, a, b): ...
❌ Won't work:
def func(): # defined in function scope a_parameters = parametrize("a", (4, 5)) class TestSomething: print('a_parameters' in {**globals(), **locals()}) # False @a_parameters # accessed in class body scope def test_method(self, a, b): ...
RuntimeError: Unable to find any parametrizes in decorators, please rewrite decorator name to match any of detected names @{'parametrize'}
-
$ cat test.pyimport unittest from parametrize import parametrize class TestSomething(unittest.TestCase): @parametrize('a', (1, 2)) def test_something(self, a): self.assertIsInstance(a, int)
✅ 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): 2 passed
❌ Won't work:
$ python -m unittest test.TestSomething.test_somethingTraceback (most recent call last): ... TypeError: don't know how to make test from: test_something[...]