编写抽象类的单元测试

时间:2016-04-04 22:14:35

标签: python unit-testing

考虑这个例子:

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中测试抽象类有通用的方法吗?

4 个答案:

答案 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类型中,只要特定实例响应特定行为,就不应该这样做。强制它成为特定抽象类的孩子。