Как заставить работать ввод аннотации на mypy с параметрами распаковки для параметризованного теста pytest?

У меня есть простой метод на my_module.py с подписью:

def my_method(value: float, extra: str = "something") -> str:
    pass

Затем у меня есть параметризованный тест на test_my_module.py для него:

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

Когда я запускаю тесты динамического типа, такие как typeguard, он проходит. Но с mypy я получаю эти [arg-type] ошибки:

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
                                   ^

Не совсем уверен, как мне аннотировать эти типы, чтобы они прошли. Обратите внимание, что это минимальный пример, поэтому я хочу вставить распаковку списка аргументов params в список, который я создаю для параметризации тестов.

Какие-нибудь мысли?


person staticdev    schedule 15.02.2021    source источник
comment
Попробуйте аннотировать свою тестовую функцию как def test_my_method(params: Union[Tuple[float], Tuple[float, str]], expected: str) -> None:   -  person Samwise    schedule 16.02.2021


Ответы (2)


Я бы рекомендовал представлять ваши переменные аргументы в виде кортежа, а не списка. типизация в настоящее время не позволяет спискам иметь определенные типы в определенных индексах, однако кортеж фиксированного размера может указывать типы для своих измерений.

Две формы, которые у вас есть в настоящее время, представляют собой либо одно число с плавающей запятой: Tuple[float], либо число с плавающей запятой и строку: Tuple[float, str] — путем преобразования вашего вызова параметризации и аргументов в это должно работать хорошо:

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

также может иметь смысл разделить ваши тесты - вы можете чрезмерно использовать параметризацию здесь, и может быть более понятно иметь один набор тестов для ваших фиксированных аргументов и другой набор тестов для вашего необязательный аргумент (вместо того, чтобы пытаться объединить все эти тесты с помощью parametrize) -- возможно, что-то вроде этого:

@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
person Anthony Sottile    schedule 16.02.2021

Вы можете аннотировать Tuple вместо List:

@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

Это обещает фиксированное количество элементов, и, следовательно, средство проверки статического типа может сделать вывод, что подпись совместима.

person a_guest    schedule 15.02.2021