使用python mock统计方法调用次数

14 投票
2 回答
12735 浏览
提问于 2025-04-17 21:58

我刚开始使用Python的模拟框架。我想要做的就是统计一个方法被调用的次数,但又不想去掉调用这个方法的实际效果。

比如,在这个简单的计数器例子中,我希望能增加计数器的值,又能记录这个方法被调用了:

import unittest
import mock


class Counter(object):
    def __init__(self):
        self.count = 0

    def increment(self):
        self.count += 1


class CounterTest(unittest.TestCase):
    def test_increment(self):
        c = Counter()
        c.increment()
        self.assertEquals(1, c.count)

    def test_call_count(self):

        with mock.patch.object(Counter, 'increment') as fake_increment:
            c = Counter()
            self.assertEquals(0, fake_increment.call_count)
            c.increment()
            self.assertEquals(1, fake_increment.call_count)

            # increment() didn't actually get called.
            self.assertEquals(1, c.count)  # Fails.

if __name__ == '__main__':
    unittest.main()

有没有办法让mock在记录了调用之后,继续调用被模拟的方法,或者只是表示我想保留这个模拟函数的效果呢?

2 个回答

3

我对模拟(mock)这块不是特别有经验,但我用一个函数包装器实现了这个功能,而不是使用默认的 MagicMock

class FuncWrapper(object):
    def __init__(self, func):
        self.call_count = 0
        self.func = func

    def __call__(self, *args, **kwargs):
        self.call_count += 1
        return self.func(*args, **kwargs)

class CounterTest(unittest.TestCase):
    def test_call_count(self):

        c = Counter()
        new_call = FuncWrapper(c.increment)
        with mock.patch.object(c, 'increment', new=new_call) as fake_increment:
            print fake_increment
            self.assertEquals(0, fake_increment.call_count)
            c.increment()
            self.assertEquals(1, fake_increment.call_count)

            self.assertEquals(1, c.count)  # Fails.

当然,这个 FuncWrapper 非常简单。它只是用来计算调用次数,然后把控制权交回给原来的函数。如果你需要同时测试其他内容,就得在 FuncWrapper 类里添加一些东西。我还只是对一个类的实例进行了修改,而不是整个类。这样做的主要原因是我需要在 FuncWrapper 里用到一个实例方法。

1其实我刚开始学习这方面的知识——算你们被警告了 ;-).

12

只需使用 wraps:

c = Counter()
with mock.patch.object(Counter, 'increment', wraps=c.increment) as fake_increment:

如果你稍后再初始化 c,可能会出现一些绑定问题,因为传给 wraps 的函数不会知道 self 是什么。

撰写回答