在Python中测试私有方法:单元测试还是功能测试?

19

在阅读有关Python中测试私有方法的内容时,特别是参考此答案后,似乎最好只测试公共接口。然而,我的类看起来像这样:

class MyClass:

  def __init__(self):
    # init code

  def run(self):
    self.__A()
    self.__B()
    self.__C()
    self.__D()

  def __A(self):
    # code for __A

  def __B(self):
    # code for __B

  def __C(self):
    # code for __C

  def __D(self):
    # code for __D

基本上,我创建了一个类来通过一系列函数处理一些输入数据。在这种情况下,逐个测试每个私有函数会很有帮助,而不将它们公开为公共函数。如果单元测试不能执行私有函数,该怎么做呢?

3个回答

60

当Python组装实际执行的代码时,它会对变量名进行一些修改。因此,如果你在MyClass类中有一个私有方法__A,则需要在单元测试中像这样运行它:

from unittest import TestCase

class TestMyClass(TestCase):
    def test_private(self):
        expected = 'myexpectedresult'
        m = MyClass()
        actual = m._MyClass__A
        self.assertEqual(expected, actual)

有人提出了关于所谓的“受保护”值的问题,这些值由单个下划线标记。这些方法名不会被破坏,这可以很简单地证明:

from unittest import TestCase

class A:

    def __a(self):
        return "myexpectedresult"

    def _b(self):
        return "a different result"


class TestMyClass(TestCase):

    def test_private(self):
        expected = "myexpectedresult"
        m = A()
        actual = m._A__a()
        self.assertEqual(expected, actual)

    def test_protected(self):
        expected = "a different result"
        m = A()
        actual = m._b()
        self.assertEqual(expected, actual)
        # actual = m._A__b() # Fails
        # actual = m._A_b()  # Fails

3
正是我在寻找的答案,应该被接受作为标准答案。 - Simon Ninon
如果方法只有一个下划线前缀呢? - JavaSa
1
@JavaSa 我已经更新了答案来回答那个问题。 - Nathaniel Ford
1
感谢 @NathanielFord。 - user_dev

16

首先,你可以访问"私有"内容,不是吗?(或者我漏掉了什么?)

>>> class MyClass(object):
...     def __init__(self):
...             pass
...     def __A(self):
...             print('Method __A()')
... 
>>> a=MyClass()
>>> a
<__main__.MyClass object at 0x101d56b50>
>>> a._MyClass__A()
Method __A()

但是,如果你必须测试内部功能,你可以在MyClass中编写一个测试函数:


class MyClass(object):
    ...
    def _method_for_unit_testing(self):
        self.__A()
        assert <something>
        self.__B()
        assert <something>
        ....

这并不是最优雅的方式,但只需要在类底部添加几行代码。


36
不应该将测试代码放在生产代码里。 - Nathaniel Ford
2
这是编写代码的极其危险的方式 - 生产环境中不应该有任何用于测试的代码 - 至少会提供错误的结果,最糟糕的情况下可能会导致该代码在生产环境中意外运行。请注意,任何搜索此内容的人都不要使用此答案。 - lynkfox

-1

你可能只需要测试run()方法。大多数类都会有内部方法——在这种情况下,__A()__B()__C,()__D()中的所有代码是否实际上在run()中并不重要。如果你怀疑或发现问题,那么你可能想切换到调试器方面并测试私有方法。


在这种情况下,我需要使用变量初始化我的类,调用run并测试结果。但是如果我想测试run中的每个步骤(每个步骤==每个函数),该怎么办? - darksky
然后,您必须按照BenDundee的说明访问它们。 - pydsigner
由于我收到了这个答案的负评,我想指出我已经按照问题的要求进行了回答:“在这种情况下,测试私有函数而不是测试公共运行(run)函数是否最好?” - pydsigner
1
你基本上建议他跳过测试子程序,或者只手动测试它们。这不是一个好习惯;单元测试应该尽可能地测试紧密的序列,并且您的测试(单元测试和集成测试)应该尽可能地进行覆盖。因此,了解如何测试私有和受保护的方法是有用的,而建议跳过这样做并不好。 - Nathaniel Ford
另一方面,单元测试应该测试“行为”,而不是“实现”,因此测试应该执行公共 API,而不是私有方法。 - snakecharmerb
1
即使是私有方法也应该通过行为测试进行测试,通过行为进行测试并不意味着从API端点进行测试。这意味着测试正在被测试的单元的行为,而不是传递给它的数据。私有/受保护的函数应该经过行为测试,如果您在测试时遇到麻烦,则应重新考虑如何编写代码以使其更易于测试。 - lynkfox

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接