在Python unittest中如何无mock检查函数是否被调用?

1 投票
2 回答
2105 浏览
提问于 2025-04-18 08:18

如果我有一个像下面这个的类,并且我想测试这个类里的 bar 函数的各种情况,我该怎么做才能不去模拟那些私有函数呢?换句话说,在 Python 的 unittest 库中,我怎样才能做到类似于这个:

def test_bar():
    f = Foo()
    f.bar(3)
    expect(self._is_positive_number).toBeCalled()

foo.py

class Foo():
    def bar(self, x):
       if type(x) is not int:
           print('Please enter a valid integer')
           return False

       if x > 0:
           self._is_positive_number()
       elif x == 0:
           self._is_zero()
       else 
           self._is_negative()

    def _is_positive_number(self):
        print('Positive')
        return True

    def _is_zero(self):
        print('Zero')
        return True

    def _is_negative_number(self):
        print('Negative')
        return True

2 个回答

2

使用mock库是一个比较好的选择。

下面是一个完整的示例,展示了三个私有方法。你可以选择更短的名字,如果你喜欢的话,但我觉得用明确的名字更好。需要注意的是,为了安全起见,你应该确认不仅是你想要的方法被调用了,还要确保其他私有方法没有被调用:

from unittest import TestCase
from mock import Mock


class MyTestCase(TestCase):
    def setUp(self):
        self.instance = Foo()
        self.instance._is_positive_number = Mock()
        self.instance._is_negative_number = Mock()
        self.instance._is_zero = Mock()

    def test_positive(self):
        self.instance.bar(3)
        self.assertTrue(self.instance._is_positive_number.called)
        self.assertFalse(self.instance._is_negative_number.called)
        self.assertFalse(self.instance._is_zero.called)

    def test_negative(self):
        self.instance.bar(-3)
        self.assertFalse(self.instance._is_positive_number.called)
        self.assertTrue(self.instance._is_negative_number.called)
        self.assertFalse(self.instance._is_zero.called)

    def test_zero(self):
        self.instance.bar(0)
        self.assertFalse(self.instance._is_positive_number.called)
        self.assertFalse(self.instance._is_negative_number.called)
        self.assertTrue(self.instance._is_zero.called)
2

据我所知,如果不模拟私有方法,是无法做到这一点的。不过,mock库(在标准库中从3.3版本开始提供,其他版本需要单独安装)让这个过程变得相对简单:

try:
    # Python 3.3 or later
    import unittest.mock as mock
except ImportError:
    # Make sure you install it first
    import mock

class TestFoo(unittest.TestCase):
    def setUp(self):
        self.f = Foo()

    def test_bar(self):
        with mock.patch.object(self.f, '_is_positive_number') as is_pos:
            self.f.bar(3)
            self.assertTrue(is_pos.called)

撰写回答