繁体   English   中英

编写抽象类的单元测试

[英]Writing unittests for abstract classes

考虑这个例子:

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

我的任务是为ABC类编写单元测试(特别是对于do_stuff )。

但是,如果我不能直接使用它的一些方法,我如何测试A类? 我应该只测试BC类(它们有helper_method实现),还是可以在Python中测试抽象类?

你没有真正的抽象基类,至少就语言而言。 没有什么能阻止你实例化它。

a = A()

如果您使用abc模块来定义无法实例化的类:

class A(metaclass=abc.ABCMeta):
    ...

那么你可以通过覆盖它的一组抽象方法来使A实例化:

A.__abstractmethods__ = frozenset()
a = A()
# test away

在任何一种情况下,您仍然可以测试抽象方法是否引发NotImplementedError

try:
    a.helper_method()
except NotImplementedError:
    print("Test passed")
else:
    print("Test failed")

或根据需要测试其默认实现。

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()

你应该测试逻辑,而不是实现。 A的do_stuff()方法本身没有逻辑,对吗? 它的作用取决于你是在处理B还是C.相反,在我看来,测试B和C的do_stuff()方法会更有意义 - 你知道它们应该做什么。

由于@chepner已经回答了你的问题,不是为了离题,但是你应该尽量避免在Python中使用抽象类。 抽象类不会或不应该在Dynamic DuckTyped语言(例如Python,Ruby等)中用于太多目的。在Duck类型中,只要特定实例响应特定行为,就不应强制它成为孩子一个特定的抽象类。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM