I generally use the pytest.mark.parametrize decorator when writing unit
tests. It occurred to me that when testing functions that raise exceptions, I
could do something like the following:
bar.py:
def foo(n: int, threshold: int = 1) -> int:
if n >= threshold:
return n
else:
raise ValueError(f'n: {n} < {threshold} (threshold)')
test_bar.py:
import pytest
from bar import foo
test_foo_data = [
(1, {}, 1),
(0, {'threshold': 0}, 0),
(0, {}, None),
(1, {'threshold': 2}, None),
]
@pytest.mark.parametrize('n, params, expectation',
test_foo_data)
def test_foo(n, params, expectation):
if expectation is not None:
assert foo(n, **params) == expectation
else:
with pytest.raises(ValueError):
foo(n, **params)
results:
$ py.test
============================= test session starts ==============================
platform darwin -- Python 3.7.6, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: /path/to/scripts
plugins: cov-2.9.0, hypothesis-5.19.0, quickcheck-0.8.4
collected 4 items
test_bar.py .... [100%]
============================== 4 passed in 0.02s ===============================
(I wouldn't necessarily check against None to switch between assert and
with pytest.raises ..., but in this case, it seemed straightforward.) The
question is just because I can do this, should I do it, or would it be
better to write separate test functions for inputs that do/don't raise
exceptions. The latter strikes me as slightly more tedious, but I'm unsure of
best practices in this case.