MOCK

import unittest
from unittest.mock import MagicMock
class A(unittest.TestCase):
    def m1(self):
        val = self.m2()
        self.m3(val)
    def m2(self):
        pass
    def m3(self, val):
        pass
    def test_m1(self):
        a = A()
        a.m2 = MagicMock(return_value="custom_val")
        a.m3 = MagicMock()
        a.m1()
        self.assertTrue(a.m2.called) #验证m2被call过
        a.m3.assert_called_with("custom_val") #验证m3被指定参数call过
        
if __name__ == '__main__':
    unittest.main(argv=['first-arg-is-ignored'], exit=False)

Mock Side Effect

可以根据不同的输入,返回不同的数值,而不只是一个 return_value。

from unittest.mock import MagicMock
def side_effect(arg):
    if arg < 0:
        return 1
    else:
        return 2
mock = MagicMock()
mock.side_effect = side_effect
mock(-1)
1
mock(1)
2

Patch

可以应用 Python 的 decoration 模式或是 context manager 概念,快速自然地 mock 所需的函数。

from unittest.mock import patch
@patch('sort')
def test_sort(self, mock_sort):
    ...
    ...

在这个 test 里面,mock_sort 替代 sort 函数本身的存在,所以,可以像开始提到的 mock object 一样,设置 return_value 和 side_effect。

另一种 patch 的常见用法,是 mock 类的成员函数,比如说一个类的构造函数非常复杂,而测试其中一个成员函数并不依赖所有初始化的 object。它的用法如下:

with patch.object(A, '__init__', lambda x: None):

代码应该也比较好懂。在 with 语句里面,通过 patch,将 A 类的构造函数 mock 为一个 do nothing 的函数,这样就可以很方便地避免一些复杂的初始化(initialization)。