官方文档 : https://docs.python.org/dev/library/unittest.mock.html
Mock
Mock类库是一个专门用于在unittest过程中制作(伪造)和修改(篡改)测试对象的类库,制作和修改的目的是避免这些对象在单元测试过程中依赖外部资源(网络资源,数据库连接,其它服务以及耗时过长等).
安装
Python 2.7中没有集成mock库,Python3中的unittest中集成了mock库.
Python 2.7环境下pip安装:
$ pip install mock
快速使用
>>> from mock import MagicMock #MagicMock为Mock的子类
>>> thing = ProductionClass()
>>> thing.method = MagicMock(return_value=3)
#指定返回3
>>> thing.method(3, 4, 5, key='value')
3
>>> thing.method.assert_called_with(3, 4, 5, key='value')
#断言输入是否为3,4,5,key='value',否则报错
示例
#module.py
class Count():
def add(self, a, b):
return a + b
测试用例:
from unittest import mock
import unittest
from module import Count
class MockDemo(unittest.TestCase):
def test_add(self):
count = Count()
count.add = mock.Mock(return_value=13, side_effect=count.add)
result = count.add(8, 8)
print(result)
count.add.assert_called_with(8, 8)
self.assertEqual(result, 16)
if __name__ == '__main__':
unittest.main()
count.add = mock.Mock(return_value=13, side_effect=count.add)
side_effect参数和return_value是相反的。它给mock分配了可替换的结果,覆盖了return_value。简单的说,一个模拟工厂调用将返回side_effect值,而不是return_value。
所以,设置side_effect参数为Count类add()方法,那么return_value的作用失效。
测试依赖
例如,我们要测试A模块,然后A模块依赖于B模块的调用。但是,由于B模块的改变,导致了A模块返回结果的改变,从而使A模块的测试用例失败。其实,对于A模块,以及A模块的用例来说,并没有变化,不应该失败才对。
通过mock模拟掉影响A模块的部分(B模块)。至于mock掉的部分(B模块)应该由其它用例来测试。
# function.py
def add_and_multiply(x, y):
addition = x + y
multiple = multiply(x, y)
return (addition, multiple)
def multiply(x, y):
return x * y
然后,针对 add_and_multiply()函数编写测试用例。func_test.py
import unittest
import function
class MyTestCase(unittest.TestCase):
def test_add_and_multiply(self):
x = 3
y = 5
addition, multiple = function.add_and_multiply(x, y)
self.assertEqual(8, addition)
self.assertEqual(15, multiple)
if __name__ == "__main__":
unittest.main()
add_and_multiply()函数依赖了multiply()函数的返回值。如果这个时候修改multiply()函数的代码。
def multiply(x, y):
return x * y + 3
python3 func_test.py
F
======================================================================
FAIL: test_add_and_multiply (main.MyTestCase)
Traceback (most recent call last):
File "fun_test.py", line 19, in test_add_and_multiply
self.assertEqual(15, multiple)
AssertionError: 15 != 18
Ran 1 test in 0.000s
FAILED (failures=1)
测试用例运行失败了,然而,add_and_multiply()函数以及它的测试用例并没有做任何修改,罪魁祸首是multiply()函数引起的,我们应该把 multiply()函数mock掉。
import unittest
from unittest.mock import patch
import function
class MyTestCase(unittest.TestCase):
@patch("function.multiply")
def test_add_and_multiply2(self, mock_multiply):
x = 3
y = 5
mock_multiply.return_value = 15
addition, multiple = function.add_and_multiply(x, y)
mock_multiply.assert_called_once_with(3, 5)
self.assertEqual(8, addition)
self.assertEqual(15, multiple)
if __name__ == "__main__":
unittest.main()
@patch("function.multiply")
patch()装饰/上下文管理器可以很容易地模拟类或对象在模块测试。在测试过程中,您指定的对象将被替换为一个模拟(或其他对象),并在测试结束时还原。
这里模拟function.py文件中multiply()函数。
def test_add_and_multiply2(self, mock_multiply):
在定义测试用例中,将mock的multiply()函数(对象)重命名为 mock_multiply对象。
mock_multiply.return_value = 15
设定mock_multiply对象的返回值为固定的15。
ock_multiply.assert_called_once_with(3, 5)
检查ock_multiply方法的参数是否正确。