我正在使用
pytest
为库编写一些测试。我想为库公开的每个函数尝试多个测试用例,因此我发现将类中每个方法的测试分组很方便。我想要测试的所有函数都具有相同的签名并返回相似的结果,因此我想使用超类中定义的辅助方法对结果进行一些断言。简化版本的运行方式如下:
class MyTestCase:
function_under_test: Optional[Callable[[str], Any]] = None
def assert_something(self, input_str: str, expected_result: Any) -> None:
if self.function_under_test is None:
raise AssertionError(
"To use this helper method, you must set the function_under_test"
"class variable within your test class to the function to be called.")
result = self.function_under_test.__func__(input_str)
assert result == expected_result
# various other assertions on result...
class FunctionATest(MyTestCase):
function_under_test = mymodule.myfunction
def test_whatever(self):
self.assert_something("foo bar baz")
在
assert_something
中,有必要在函数上调用 __func__()
,因为将函数分配给类属性会使其成为该类的绑定方法 - 否则 self
将作为第一个参数传递给外部库函数,它没有任何意义。
此代码按预期工作。但是,它会产生 MyPy 错误:
"Callable[[str], Any]" has no attribute "__func__"
根据我的注释,这是正确的,这不是一个安全的操作:任意 Callable 可能没有
__func__
属性。但是,我找不到任何类型注释来表明 function_under_test
变量引用一个方法,因此将始终具有 __func__
。我是否忽略了一个,或者是否有另一种方法来调整我的注释或访问以使其与类型检查一起使用?
当然,还有很多其他方法可以解决这个问题,其中一些甚至可能更干净(使用
Any
类型,跳过类型检查,使用私有方法返回被测试的函数而不是使其成为一个类变量,使辅助方法成为函数等)。我更感兴趣的是是否有注释或其他 mypy 技巧可以让这段代码正常工作。
Callable 仅确保您的对象具有
__call__
方法。
你的问题是你的电话
self.function_under_test.__func__(input_str)
你应该只调用你的函数self.function_under_test(input_str)
请参阅下面的示例,没有 mypy 投诉(v0.910)
from typing import Any, Callable, Optional
class MyTestCase:
function_under_test: Optional[Callable] = None
def myfunction_wrap(self, *args, **kwargs):
raise NotImplementedError
def assert_something(self, input_str: str, expected_result: Any) -> None:
if self.function_under_test is None:
raise AssertionError(
"To use this helper method, you must set the function_under_test"
"class variable within your test class to the function to be called.")
result = self.myfunction_wrap(input_str)
assert result == expected_result
# various other assertions on result...
def myfunction(a: str) -> None:
...
class FunctionATest(MyTestCase):
def myfunction_wrap(self, *args, **kwargs):
myfunction(*args, **kwargs)
def test_whatever(self):
self.assert_something("foo bar baz")
Edit1:错过了问题的要点,将函数移到了包装函数中