英文:
How to tell mypy that I am explicitly testing for an incorrect type?
问题
考虑以下玩具示例:
import pytest
def add(a: float) -> float:
if not isinstance(a, float):
raise ValueError("a must be of type float")
return 10 + a
def test_add_wrong_type() -> None:
with pytest.raises(ValueError) as err:
add("foo") # mypy is complaining here
assert str(err.value) == "a must be of type float"
“mypy” 报错如下:
“add”的第1个参数具有不兼容的类型“str”;预期为“float” [arg-type]
“mypy”是正确的。然而,在这种情况下,我是故意输入了错误的类型。我如何告诉“mypy”忽略这一行?
换句话说,有什么一种符合 Python 风格的方式来测试不正确的输入类型?
英文:
Consider the following toy example:
import pytest
def add(a: float) -> float:
if not isinstance(a, float):
raise ValueError("a must be of type float")
return 10 + a
def test_add_wrong_type() -> None:
with pytest.raises(ValueError) as err:
add("foo") # mypy is complaining here
assert str(err.value) == "a must be of type float"
mypy
is complaining as follows:
Argument 1 to "add" has incompatible type "str"; expected "float" [arg-type]
Well, mypy
is correct. However, in that case I put in an incorrect type on purpose. How can I tell mypy
to ignore this line`?
Put it differently, what is a pythonic way to test for an incorrect input type?
答案1
得分: 1
以下是翻译好的内容:
Python类型注解的测试社区至少有两种单元测试类型注解的方法。请注意,# type: ignore
和 typing.cast
不是测试它们 - 它们是忽略它们和覆盖它们的方式,分别是:
根据您的目的,您可能想选择以下之一:
stubtest
,它与mypy一起提供。这在typeshed中广泛使用,但更适用于检查.pyi
文件中的任何给定类型注解是否与运行时实现匹配。- pytest-mypy-plugins,听起来是您在示例中应该选择的。从
README.md
中获取一个示例,您可能想编写类似于以下内容:# test_add.yml - case: test_add_wrong_type main: | from my_module import add add("") # E: Argument 1 to "add" has incompatible type "str"; expected "float" [arg-type]
(我没有测试过这个 - 他们的文档没有提到内联测试错误。我从
mypy/test-data/unit
中的示例中获取了# E: ...
的语法)。
您还可以将mypy
作为pytest测试套件的运行时依赖项,如果是这种情况,您可能不需要pytest-mypy-plugins
- 他们的测试套件在他们自己的非公开pytest挂钩上运行,您可以直接在您的conftest.py
中编写类似于他们的check-*.test
文件的示例。
英文:
The Python typing community has at least 2 ways of unit-testing type annotations. Note that # type: ignore
and typing.cast
are not testing them - they're ignoring them and overriding them, respectively.
Depending on your purpose, you'd want to go for one of the following:
stubtest
, which ships with mypy. This is extensively used by typeshed, but is more suited to check that any given type annotations in.pyi
files match with the runtime implementation.- pytest-mypy-plugins, which sounds like what you should go for in your example. Taking an example from
README.md
, you'd probably want to write something like this:# test_add.yml - case: test_add_wrong_type main: | from my_module import add add("") # E: Argument 1 to "add" has incompatible type "str"; expected "float" [arg-type]
(I haven't tested this - their documentation doesn't mention inline-testing errors. I took the
# E: ...
syntax from the examples inmypy/test-data/unit
).
You can also make mypy
a runtime dependency for your pytest test suite, in which case you probably don't need pytest-mypy-plugins
- their test suite runs on their own non-exposed pytest hooks, which you can import directly in your conftest.py
. Just write out examples like their check-*.test
files.
答案2
得分: 0
Use typing.cast
to assert that "foo" is a float.
from typing import cast
def test_add_wrong_type() -> None:
with pytest.raises(ValueError) as err:
add(cast(float, "foo"))
assert str(err.value) == "a must be of type float"
cast
doesn't do type conversions; at runtime, it simply returns its second argument. Its purpose is to provide a standard way to assert a static type for a given value, independent of whatever type checker you might be using.
英文:
Use typing.cast
to assert that "foo"
is a float
.
from typing import cast
def test_add_wrong_type() -> None:
with pytest.raises(ValueError) as err:
add(cast(float, "foo"))
assert str(err.value) == "a must be of type float"
cast
doesn't do type conversions; at runtime, it simply returns its second argument. Its purpose is to provide a standard way to assert a static type for a given value, independent of whatever type checker you might be using.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论