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

21 投票
3 回答
43484 浏览
提问于 2025-04-17 19:20

在阅读关于如何测试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 个回答

-1

你可能只需要测试一下 run() 这个方法。大多数类里面都会有一些内部方法,而在这种情况下,__A()__B()__C()__D() 这些方法里的代码是否都在 run() 里面其实并不重要。如果你怀疑或者发现了问题,那你可以切换到调试模式,去测试一下那些私有方法。

62

在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
14

首先,你可以访问那些“私有”的东西,对吧?(或者我是不是漏掉了什么?)

>>> 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>
        ....

这确实不是最优雅的做法,但在你类的底部只需要几行代码。

撰写回答