mock介绍
mock允许用模拟对象替换系统中真实对象,并对它们已使用的方式进行断言。
在进行单元测试的时候,会遇到以下问题:
•接口的依赖;
•外部接口调用;
•测试环境非常复杂。
且单元测试应该只针对当前单元进行测试, 所有的内部或外部的依赖应该是稳定的, 已经在别处进行测试过的.使用mock 就可以对外部依赖组件实现进行模拟并且替换掉, 从而使得单元测试将焦点只放在当前的单元功能。
以下一个简单的示例:
调用mock并指定 reutrn_value 创建一个mock对象,然后mock 掉 say_hello 函数对象后,这个方法的一切动作,比如 print 就变得没有意义了,因为最后这个方法只会做一件事就是返回我们为其指定的返回值。
from unittest.mock import Mock def say_hello(word): print(f"Hello {word}") say_hello("China") mock=Mock(return_value="PYTHON OH YEAH") say_hello=mock say_hello("China")
mock模块的基本使用
return_vaule
mock 对象的 return_vaule 的作用:它将忽略 mock 对象的行为,指定其返回值。
modular.py
class Count(): def add(self): pass
mock_demo01.py
from unittest import mock import unittest from modular import Count # test Count class class TestCount(unittest.TestCase): def test_add(self): count = Count() count.add = mock.Mock(return_value=13) result = count.add(8,5) self.assertEqual(result,13) if __name__ == '__main__': unittest.main()
假设Count计算类没有实现,原本add() 方法要实现两数相加。但这个功能还没有完成。这时就可以借助mock对其进行测试。
count = Count()
首先,调用被测试类Count() 。
count.add = mock.Mock(return_value=7)
通过Mock类模拟被调用的方法add()方法,return_value 定义add()方法的返回值。
result = count.add(2,5)
接下来,相当于在正常的调用add()方法,传两个参数2和5,然后会得到相加的结果7。然后,7的结果是我们在上一步就预先设定好的。
self.assertEqual(result,7)
最后,通过assertEqual()方法断言,返回的结果是否是预期的结果7。
side_effect
mock 对象的side_effect 的作用:通过side_effect指定mock对象的副作用,这个副作用就是当你调用这个mock对象时会调用的函数,也可以选择抛出一个异常,来对程序的错误状态进行测试。
from unittest.mock import Mock def say_hello(word): print(f"Hello {word}") mock=Mock() #指定为函数 mock.side_effect = say_hello mock('china') #指定为异常 mock.side_effect = KeyError('This is b') #Exception("Raise Exception") mock()
另外也可以通过为side_effect指定一个列表,这样在每次调用时会依次返回,如下:
from unittest.mock import Mock mock=Mock(side_effect = [1, 2, 3]) print(mock()) print(mock()) print(mock())
patch装饰器
它是一个装饰器,需要把你想模拟的函数写在里面,然后在后面的单元测试案例中为它赋一个具体实例,再用 return_value 来指定模拟的这个函数希望返回的结果就可以了,后面就是正常单元测试代码。
@mock.pathc.object(类名,“类中函数名”)
from unittest import mock import unittest class Count(): def add(self): pass # test Count class class TestCount(unittest.TestCase): @mock.patch.object(Count, "add") def test_add(self, mock_add): mock_add. return_value = 13 result = mock_add() self.assertEqual(result,13) if __name__ == '__main__': unittest.main()
@mock.pathc(模块名,“函数名”)
linux_tool.py
import re def send_shell_cmd(): return "Response from send_shell_cmd function" def check_cmd_response(): response = send_shell_cmd() print("response: {}".format(response)) return re.search(r"mock_send_shell_cmd", response)
测试代码:
from unittest import TestCase, mock import linux_tool class TestLinuxTool(TestCase): def setUp(self): pass def tearDown(self): pass @mock.patch("linux_tool.send_shell_cmd") def test_check_cmd_response(self, mock_send_shell_cmd): mock_send_shell_cmd.return_value = "Response from emulated mock_send_shell_cmd function" status = linux_tool.check_cmd_response() print("check result: %s" % status) self.assertTrue(status)
如果 patch 多个外部函数,那么调用遵循自下而上的规则,比如:
@mock.patch("function_C") @mock.patch("function_B") @mock.patch("function_A") def test_check_cmd_response(self, mock_function_A, mock_function_B, mock_function_C): mock_function_A.return_value = "Function A return" mock_function_B.return_value = "Function B return" mock_function_C.return_value = "Function C return" self.assertTrue(re.search("A", mock_function_A())) self.assertTrue(re.search("B", mock_function_B())) self.assertTrue(re.search("C", mock_function_C()))
一个示例
Count类中add_and_multiply依赖multiply,由于multiply并没有实现,这时候可以使用mock替换multiply:
from unittest import mock import unittest class Count(): def add_and_multiply(self,x, y): addition = x + y multiple = self.multiply(x, y) return (addition, multiple) def multiply(self,x, y): pass # test Count class class TestCount(unittest.TestCase): @mock.patch.object(Count, "multiply") def test_add(self, mock_multiply): mock_multiply. return_value = 40 count = Count() addition,multiple = count.add_and_multiply(5,8) self.assertEqual(addition,13) self.assertEqual(multiple, 40) if __name__ == '__main__': unittest.main()