I have a simple method on my_module.py
with the signature:
def my_method(value: float, extra: str = "something") -> str:
pass
Then I have a parameterized test on test_my_module.py
for it:
from typing import List
from typing import Union
import pytest
import my_module
@pytest.mark.parametrize(
"params, expected",
[
([1.0], "result1"),
([2.0], "result2"),
([2.0, "extra"], "result3")
],
)
def test_my_method(params: List[Union[int, str]], expected: str) -> None:
assert my_module.my_method(*params) == expected
When I run dynamic type tests such as typeguard
it passes. But with mypy
I get these [arg-type]
errors:
tests/test_my_module.py: note: In function "test_my_method":
tests/test_my_module.py:50:30: error: Argument 1 to "my_method" has incompatible type "*List[Union[int, str]]"; expected "float"
[arg-type]
assert my_module.my_method(*params) == expected
^
tests/test_my_module.py:50:30: error: Argument 1 to "my_method" has incompatible type "*List[Union[int, str]]"; expected "str"
[arg-type]
assert my_module.my_method(*params) == expected
^
Not really sure how should I annotate these types to make it pass. Note that this is a minimal example, so I want to stick unpacking the list of arguments params
in the list I create to parameterize the tests.
Any thoughts?
I'd recommend representing your variable arguments as a tuple instead of a list. typing does not currently allow for lists to have specific types at specific indicies, however a fixed-size tuple can specify types for its dimensions
The two shapes you have currently are either a single float: Tuple[float]
or a float and a string: Tuple[float, str]
-- by converting your parametrize call and arguments to this it should work nicely:
from typing import Tuple
from typing import Union
import pytest
import my_module
@pytest.mark.parametrize(
"params, expected",
[
((1.0,), "result1"),
((2.0,), "result2"),
((2.0, "extra"), "result3")
],
)
def test_my_method(params: Union[Tuple[float], Tuple[float, str]], expected: str) -> None:
assert my_module.my_method(*params) == expected
it may also make sense to split up your tests -- you might be over-using parametrize here and it may be more clear to have one set of tests for your fixed arguments and another set of tests for your optional argument (instead of trying to jam all of those tests together with parametrize) -- perhaps something like this:
@pytest.mark.parametrize(
"arg, expected",
[
(1.0, "result1"),
(2.0, "result2"),
],
)
def test_my_method_one_arg(arg: float, expected: str) -> None:
assert my_module.my_method(arg) == expected
@pytest.mark.parametrize(
"arg, extra, expected",
[
(1.0, "extra", "result3"),
(2.0, "extra", "result4"),
],
)
def test_my_method_extra_arg(arg: float, extra: str, expected: str) -> None:
assert my_module.my_method(arg, extra) == expected