考虑这个例子:
class A:
def do_stuff(self):
# ...
some_var = self.helper_method()
# ...
def helper_method(self):
# This method must be implemented by subclass
raise NotImplementedError()
class B(A):
def helper_method(self):
# implementation for class B
class C(A):
def helper_method(self):
# implementation for class C
我的任务是为A
,B
和C
类编写单元测试(尤其是do_stuff
)。
但是,如果我不能直接使用某些方法,我怎么能测试A
类呢?
我应该只测试B
和C
类(具有helper_method
的实现)
或者在Python中测试抽象类有通用的方法吗?
答案 0 :(得分:3)
至少就语言而言,你真的没有抽象的基类。没有什么能阻止你实例化它。
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
如果您使用a = A()
模块来定义您无法实例化的类:
abc
然后你可以通过覆盖它的一组抽象方法使class A(metaclass=abc.ABCMeta):
...
可实例化:
A
在任何一种情况下,您仍然可以测试抽象方法是否引发A.__abstractmethods__ = frozenset()
a = A()
# test away
NotImplementedError
或根据需要测试其默认实现。
答案 1 :(得分:2)
do_stuff
上存在 A
,因此请在A
上进行测试。辅助方法存在于具体类中,因此在那里进行测试。您可以使用unittest.mock
模块临时修补抽象类,以便它可以与您的测试一起使用,还可以修补抽象方法以返回特定值 - 这样它的逻辑就不会受到测试。鉴于这一切,这就是我测试抽象类的方法。
给出一些抽象类:
from abc import abstractmethod, ABC
class MyAbstract(ABC):
def concrete_method(self):
i = self.abstract_method()
return i ** 2
@abstractmethod
def abstract_method(self):
"""return some int"""
pass
这是我测试它的方式。
from unittest import main, TestCase
from unittest.mock import patch, Mock
from module_under_test import MyAbstract
class TestMyAbstract(TestCase):
def test_cannot_instantiate(self):
"""showing we normally can't instantiate an abstract class"""
with self.assertRaises(TypeError):
MyAbstract()
@patch.multiple(MyAbstract,
__abstractmethods__=set(),
abstract_method=Mock(return_value=3))
def test_concrete_method(self):
"""patch abstract class and its abstract methods for duration of the test"""
# given
my_abstract = MyAbstract()
expected = 9
# when
actual = my_abstract.concrete_method()
# then
self.assertEqual(actual, expected)
if __name__ == "__main__":
main()
答案 2 :(得分:1)
你应该测试逻辑,而不是实现。一个do_stuff()方法本身没有逻辑,对吗?它的作用取决于你是否正在处理B或C.相反,在我看来,测试B和C的do_stuff()方法会更有意义 - 你知道他们应该做什么
答案 3 :(得分:1)
由于@chepner已经回答了你的问题,不是要离题,但是你应该尽量避免在Python中使用抽象类。抽象类在动态DuckTyped语言(如Python,Ruby等)中没有或更不应该用于多用途。在Duck类型中,只要特定实例响应特定行为,就不应该这样做。强制它成为特定抽象类的孩子。