1、简介
什么是mock?
mock翻译过来有模拟的意思。这里介绍的mock是辅助单元测试的一个模块。它允许你用模拟对象替换你的系统的部分,并对它们已使用的方式进行断言。
什么时候使用mock?
场景1:比如有A和B两个模块,A模块中有调用到B模块的方法,但是B模块中被A模块调用的方法由于一些原因需要被修改,然而我们又不想影响A模块的功能测试,这时候就用到了mock,用来模拟出一个假的B模块。
场景2:有时需要为单元测试的初始设置准备一些其他的资源,但是这些资源又不太经常使用或者使用起来比较麻烦,此时我们就可以定义一个mock对象来模拟一个需要使用的资源,用于代替测试的准备资源。
2、安装
2.1、Python 3.3以前的版本
在Python 3.3以前的版本中,mock是一个单独模块,需要单独安装。
安装方式有2种(任选一种即可)。
1、利用pip安装
命令行输入 pip install mock
2、源码安装
下载网址:https://pypi.org/project/mock/
下载完成后,进行解压,例如:mock-2.0.0.tar.gz
如图所示,解压后的文件目录
打开命令行,跳转到解压后的目录路径,输入安装命令python setup.py install即可。
之后在代码中直接import进来就可以使用mock了。
import mock
2.2、Python 3.3及更高版本
在Python 3.3及更高版本中,mock已经被集成到了unittest单元测试框架中,所以可以直接使用。
在代码中直接import进来就可以使用mock了。
from unittest import mock
3、基本示例
Mock对象是mock模块中最重要的概念。Mock对象就是mock模块中的一个类的实例,这个类的实例可以用来替换其他的Python对象,来达到模拟的效果。
Mock对象的一般用法:
步骤1:找到你要替换的对象(一个类,或者一个函数,或者一个类实例)。
步骤2:实例化Mock类得到一个mock对象,并且设置这个mock对象的行为(比如被调用的时候返回什么值,被访问成员的时候返回什么值等)。
步骤3:使用这个mock对象替换掉我们想替换的对象,也就是步骤1中确定的对象。
步骤4:之后就可以开始写测试用例,这个时候我们可以保证我们替换掉的对象在测试用例执行的过程中行为和我们预设的一样。
下面开始讲解基本示例:
一、创建Demo.py文件。
创建被测试函数(get、send_request)
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
基本示例:被测试函数
"""
# 引入Requests库
import requests
def get(url):
# 发起GET请求
r = requests.get(url)
# 返回状态码
return r.status_code
def send_request():
# 调用get()函数
return get('https://www.baidu.com/')
二、创建MockTest.py文件。
创建TestDemo测试类。
1、不使用mock
1.1、脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
基本示例:测试类
"""
import unittest
from demo import Demo
class TestDemo(unittest.TestCase):
def test_request(self):
print(Demo.send_request())
self.assertEqual(Demo.send_request(), 200)
if __name__ == '__main__':
unittest.main(verbosity=2)
1.2、执行MockTest.py文件,运行结果:
调用Demo.send_request(),期望和实际状态码返回值一样。
2、使用mock
2.1、脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
基本示例:测试类
"""
import unittest
from unittest import mock
from demo import Demo
class TestDemo(unittest.TestCase):
def test_request_mock(self):
Demo.get = mock.Mock(return_value=404)
print(Demo.get())
self.assertEqual(Demo.send_request(),404)
if __name__ == '__main__':
unittest.main(verbosity=2)
2.2、执行MockTest.py文件,运行结果:
(1)首先实例化Mock类得到一个mock对象,并且设置这个mock对象的行为(返回值为404)。
(2)使用这个mock对象替换掉我们想替换的对象(Demo.get)。
(3)调用Demo.send_request(),期望和预设值一样(404)。
4、构造器
4.1、name
name:是mock对象的唯一标识;用来命名一个mock对象,只是起到标识作用,当你print一个mock对象的时候,可以看到它的name。
1、创建MockTest_name.py文件。
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
构造器:参数name
"""
from unittest import mock
# name定义了mock对象的唯一标示符
testMock = mock.Mock(name='MyMock')
print(testMock)
2、执行MockTest_name.py文件,运行结果:
定义mock对象唯一标识为'MyMock'。
4.2、spec
spec:设置mock对象的属性,可以是property或者方法,也可以是其他的列表字符串或者其他的python类。
1、创建MockTest_spec.py文件。
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
构造器:参数spec
"""
from unittest import mock
# 指定属性组成的list
MyList = ['username','password']
# spec设置mock对象的属性,可以是property或者方法;属性可以是一个列表字符串或者是其他的Python类
testMock = mock.Mock(spec=MyList)
print(testMock)
print(testMock.username)
print(testMock.password)
2、执行MockTest_spec.py文件,运行结果:
设置mock两个属性(username、password)
4.3、return_value
return_value:设置当这个mock对象被调用的时候,显示出的结果就是return_value的值。
创建Demo.py文件(创建被测试类:People类)。
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
被测试类
"""
# People类里有两个成员方法(一个有参数,一个无参数)、一个静态方法
class People:
def __init__(self):
self.__age = 20
def name(self,firstName,lastName):
return firstName + ' ' + lastName
def age(self):
return self.__age
@staticmethod
def class_name():
return People.__name__
4.3.1、指定某个值
创建MockTest_return_value.py文件(创建PeopleTest测试类)。
1、不使用mock
1.1、脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
构造器:参数return_value(返回固定值)
"""
from method.Demo import People
import unittest
class PeopleTest(unittest.TestCase):
def test_age(self):
# 调用被测试类People()
p = People()
print(p.age())
self.assertEqual(p.age(), 20)
if __name__ == '__main__':
unittest.main(verbosity=2)
1.2、执行MockTest_return_value.py文件,运行结果:
不使用mock,p.age()返回20。
2、使用mock
2.1、脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
常用方法:构造器,参数return_value(返回固定值)
"""
from method.Demo import People
from unittest import mock
import unittest
class PeopleTest(unittest.TestCase):
# 指定某个值
def test_age(self):
# 调用被测试类People()
p = People()
p.age = mock.Mock(return_value=50)
print(p.age())
self.assertEqual(p.age(), 50)
# 指定某个值
def test_name(self):
# 调用被测试类People()
p = People()
p.name = mock.Mock(return_value='Hello Mock')
print(p.name())
self.assertEqual(p.name(), 'Hello Mock')
if __name__ == '__main__':
unittest.main(verbosity=2)
2.2、执行MockTest_return_value.py文件,运行结果:
(1)test_age方法,mock掉age方法,让它返回50。
(2)test_name方法,mock掉name方法,让它返回Hello Mock。
4.3.2、指定某个类对象
1、创建MockTest_return_value.py文件(创建PeopleTest测试类)。
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
构造器:参数return_value(返回固定值)
"""
from method.Demo import People
from unittest import mock
import unittest
class PeopleTest(unittest.TestCase):
# 指定某个类对象
def test_People(self):
testMock = mock.Mock(return_value=People())
print(testMock)
print(testMock().class_name())
print(testMock().name('Hello','Mock'))
print(testMock().age())
if __name__ == '__main__':
unittest.main(verbosity=2)
2、执行MockTest_return_value.py文件,运行结果:
指定People类,并执行类里的方法。
4.4、side_effect
side_effect:和return_value是相反的,覆盖了return_value,也就是说当这个mock对象被调用的时候,返回的是side_effect的值,而不是return_value。
创建Demo.py文件(创建被测试类:People类)。
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
被测试类
"""
# People类里有两个成员方法(一个有参数,一个无参数)、一个静态方法
class People:
def __init__(self):
self.__age = 20
def name(self,firstName,lastName):
return firstName + ' ' + lastName
def age(self):
return self.__age
@staticmethod
def class_name():
return People.__name__
4.4.1、依次返回指定值
1、创建MockTest_side_effect.py文件(创建PeopleTest测试类)。
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
构造器:参数side_effect(它给mock分配了可替换的结果,覆盖了return_value)
"""
from method.Demo import People
from unittest import mock
import unittest
class PeopleTest(unittest.TestCase):
# 依次返回指定值
def test_age(self):
# 调用被测试类People()
p = People()
MyList = [15,16,17]
p.age = mock.Mock(return_value=50,side_effect=MyList)
print(p.age())
print(p.age())
print(p.age())
print(p.age())
if __name__ == '__main__':
unittest.main(verbosity=2)
2、执行MockTest_side_effect.py文件,运行结果:
(1)执行前3个p.age()都会返回一个值,即15、16、17。
(2)执行第4个p.age()时,因为返回值没有,则抛出异常。
4.4.2、根据参数返回指定值
1、创建MockTest_side_effect.py文件(创建PeopleTest测试类)。
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
构造器:参数side_effect(它给mock分配了可替换的结果,覆盖了return_value)
"""
from method.Demo import People
from unittest import mock
import unittest
class PeopleTest(unittest.TestCase):
# 根据参数返回指定值
def test_name(self):
# 调用被测试类People()
p = People()
values = {('a', 'b'): 'Hello Mock', ('1', '2'): 'Hello World'}
p.name = mock.Mock(side_effect=lambda x, y: values[(x, y)])
print(p.name('a', 'b'))
print(p.name('1', '2'))
self.assertEqual(p.name('a', 'b'), 'Hello Mock')
self.assertEqual(p.name('1', '2'), 'Hello World')
if __name__ == '__main__':
unittest.main(verbosity=2)
2、执行MockTest_side_effect.py文件,运行结果:
(1)执行p.name('a', 'b')方法对应值为Hello Mock。
(2)执行p.name('1', '2')方法对应值为Hello World。
4.4.3、抛出异常
1、创建MockTest_side_effect.py文件(创建PeopleTest测试类)。
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
构造器:参数side_effect(它给mock分配了可替换的结果,覆盖了return_value)
"""
from method.Demo import People
from unittest import mock
import unittest
class PeopleTest(unittest.TestCase):
# 抛出异常
def test_age_error(self):
# 调用被测试类People()
p = People()
p.age = mock.Mock(side_effect=SystemError)
print(p.age())
if __name__ == '__main__':
unittest.main(verbosity=2)
2、执行MockTest_side_effect.py文件,运行结果:
设置side_effect为SystemError异常,调用p.age()方法就会抛出异常。
如果您觉得文章还不错,请 点赞、分享收藏 一下,因为这将是我持续输出更多优质文章的最强动力!