Drop-in @pytest.mark.parametrize replacement working with unittest.TestCase
Project description
Drop-in @pytest.mark.parametrize
replacement working with unittest.TestCase
Why?
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.
Usage
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)])
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)
You don't need to use additional decorators, custom base classes or metaclasses.
Stacking parametrize decorators:
import unittest
from parametrize import parametrize
class TestSomething(unittest.TestCase):
@parametrize("x", [0, 1])
@parametrize("y", [2, 3])
def test_foo(self, x, y):
pass
test_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
OK
Note: even though the tests are always generated in the same order, the execution order is not guaranteed
Compatibility
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
.
Limitations
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:
-
All parametrization must be done via decorators
:white_check_mark: OK
@parametrize('a', (1, 2)) def f(a): ...
:x: Won't work:
def f(a): ... parametrize('a', (1, 2))(func)
RuntimeError: Unable to find any parametrizes in decorators, please rewrite decorator name to match any of detected names @{'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") def f(a, b): return a, b
:x: 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::white_check_mark: 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): ...
:x: 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'}
-
Parametrized method can be ran from command line only via pytest:
$ cat test.py
import unittest from parametrize import parametrize class TestSomething(unittest.TestCase): @parametrize('a', (1, 2)) def test_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): 2 passed
:x: Won't work:
$ python -m unittest test.TestSomething.test_something
Traceback (most recent call last): ... TypeError: don't know how to make test from: test_something[...]
-
@parametrize
cannot be used in interactive environments like REPL (It works in IPython though) -
@parametrize
cannot be used in cythonized code
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file parametrize-0.1.1.tar.gz
.
File metadata
- Download URL: parametrize-0.1.1.tar.gz
- Upload date:
- Size: 7.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.1.6 CPython/3.9.5 Linux/5.4.0-1046-azure
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | d7ac0f61b781d1eadfa81d9e57ea80d5e184078e1976b7bb052ab682d9ef35de |
|
MD5 | 06d3719f9d86458dbeb1575c871d890d |
|
BLAKE2b-256 | 3b18983fbff2728e2b23913279b7218829d43565b6e90c88363a4e76ad2456ee |
File details
Details for the file parametrize-0.1.1-py3-none-any.whl
.
File metadata
- Download URL: parametrize-0.1.1-py3-none-any.whl
- Upload date:
- Size: 7.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.1.6 CPython/3.9.5 Linux/5.4.0-1046-azure
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 618fc00d15a03df7177691e83e59aeb976b20c410ce39af5063d1839a4673645 |
|
MD5 | 498c3c75b9a681a41fd66c7c2dfc47a6 |
|
BLAKE2b-256 | ca2c0258127958773f87feaee47870627080a3e59cb8c691fcefe9fa10334114 |