Mock相关知识和简单应用
一.moco的简单应用
moco地址:https://github.com/dreamhead/moco
api文档地址: https://github.com/dreamhead/moco/blob/master/moco-doc/apis.md
启动命令: java -jar moco-runner-1.0.0-standalone.jar http -p 12306 -c foo.json
moco只关注服务器的配置,也就是客户端与服务端,或者更加具体的说就是请求和响应。
实例:
二.mock的介绍与详解
mock在python3.3之前是第三方库,在python3.3版本之后是标准库,只需要导入就可以使用。
Python3.3版本之前引入方式是
import mock
python3.3版本之后的引入方式是
from unittest import mock
Mock的意义:Mock能够让我们模拟那些在单元测试中不可用或太笨重的资源
Mock是Python中一个用于支持单元测试的库,它的主要功能是使用mock对象,以达到摸你对象的行为。Mock它可以替换Python对象。
1.使用现实情况:
1.环境由于客观原因导致无法搭建
2.搭建服务器需要大量的工作才可以
案例:
1.测试一个网站
不存在的网站www.wuya.com
200
404
500
400
401
403
模拟进行200,404,
2.测试C盘
2.Mock和MagicMock
在单元测试进行的同时,就离不开mock模块的存在,初次接触这个概念的时候会有这样的疑问:把要测的东西都模拟掉了还测试什么呢? 但在,实际生产中的项目是非常复杂的,对其进行单元测试的时候,会遇到以下问题: •接口的依赖 •外部接口调用 •测试环境非常复杂 单元测试应该只针对当前单元进行测试, 所有的内部或外部的依赖应该是稳定的, 已经在别处进行测试过的.使用mock 就可以对外部依赖组件实现进行模拟并且替换掉, 从而使得单元测试将焦点只放在当前的单元功能。
因为在为代码进行单元测试的同时,会发现该模块依赖于其他的模块,例如数据库,网络,或者第三方模块的存在,而我们对一个模块进行单元测试的目的,是测试当前模块正常工作,这样就要避开对其他模块的依赖,而mock主要作用便在于,专注于待测试的代码。而在但与测试中,如何灵活的使用mock模块是核心所在。下面便以mock为核心,结合最近所写的代码,阐述mock模块的使用
3. mock模块的使用
在mock模块中,两个常用的类型为Mock,MagicMock,两个类的关系是MagicMock继承自Mock,最重要的两个属性是return_value, side_effect。
>>> from mock import Mock
>>> fake_obj = Mock()
>>>fake_obj.return_value = 'This is a mock object'
>>> fake_obj()
'This is a mock object'
我们通过Mock()可以创建一个mock对象,通过renturn_value 指定它的返回值。即当下文出现fake_obj()会返回其return_value所指定的值。 也可以通过side_effect指定它的副作用,这个副作用就是当你调用这个mock对象是会调用的函数,也可以选择抛出一个异常,来对程序的错误状态进行测试。
>>>def b():
... print 'This is b'
...
>>>fake_obj.side_effect = b
>>>fake_obj()
This is b
>>>fake_obj.side_effect = KeyError('This is b')
>>>fake_obj()
...
KeyError: 'This is b'
如果要模拟一个对象而不是函数,你可以直接在mock对象上添加属性和方法,并且每一个添加的属性都是一个mock对象【注意,这种方式很有用】,也就是说可以对这些属性进行配置,并且可以一直递归的定义下去。
>>>fake_obj.fake_a.return_value = 'This is fake_obj.fake_a'
>>>fake_obj.fake_a()
'This is fake_obj.fake_a'
上述代码片段中fake_obj是一个mock对象,而fake_obj.fake_a的这种形式使得fake_a变成了fake_obj的一个属性,作用是在fake_obj.fake_a()调用时会返回其return_value。 另外也可以通过为side_effect指定一个列表,这样在每次调用时会依次返回,如下:
>>> fake_obj = Mock(side_effect = [1, 2, 3])
>>>fake_obj()
1
>>>fake_obj()
2
>>>fake_obj()
3
3.1 函数的如何mock
在rbd_api.py文件中如下内容:
import DAO_PoolMgr def checkpoolstat(pool_name)
ret, poolstat = DAO_PoolMgr.DAO_query_ispoolok(pool_name)
if ret != MGR_COMMON.MONGO_SUCCESS:
return ret
if poolstat is False:
return MGR_COMMON.POOL_STAT_ERROR
return MGR_COMMON.SUCCESS
要为这个函数撰写单元测试,因为其有数据库的操作,因而就需要mock 出DAO_query_ispoolok操作。 因此,我们在test_rbd_api.py文件中可以这么写:因为DAO_query_ispoolok是类DAO_PoolMgr的操作,因此可以这么写
#!/usr/bin/python
import DAO_PoolMgr
import unittest
import rbd_api as rbdAPI class TestAuxiliaryFunction(unittest.TestCase):
def setUp(self):
self.pool_name = "aaa" def tearDown(self):
self.pool_name = None
@mock.patch.object(DAO_PoolMgr, "DAO_query_ispoolok")
def test_checkpoolstat(self, mock_DAO_query_ispoolok):
mock_DAO_query_ispoolok.return_value = (MGR_COMMON.POOL_STAT_ERROR, None)
self.assert(rbdAPI.checkpoolstat(self.pool_name), MGR_COMMON.POOL_STAT_ERROR) mock_DAO_query_ispoolok.return_value = (MGR_COMMON.SUCCESS, False)
self.assert(rbdAPI.checkpoolstat(self.pool_name), MGR_COMMON.POOL_STAT_ERROR) mock_DAO_query_ispoolok.return_value = (MGR_COMMON.SUCCESS, True)
self.assert(rbdAPI.checkpoolstat(self.pool_name), MGR_COMMON.SUCCESS)
测试用例上的装饰器含义如下: @mock.pathc.object(类名,“类中函数名”),而如果想要忽略某个测试用例,则可以通过装饰器@unittest.skip(“原因”) 而对于另外一种情形则是在另外一个函数中调用了checkpoolstat函数。 如下rbd_api.py:
def checkpoolstat():
…… class Disk(Resource):
def __init__(self):
……
def delete(self, pool, img):
ret = rbd_api.checkpoolstat()
……
这样,我们在为delete函数撰写单元测试时,也可以在test_rbd_api.py中使用如下的方式:
import rbd_api class TestDisk(unittest.TestCase):
def setup():
…
def teardown():
…
@mock.patch(“rbd_api.checkpoolstat”, Mock(return_value = True))
def test_delete():
# rbd_api.checkpoolstat 已经成为一个mock对象了,调用时返回True
…
此时的装饰器应该为
@mock.patch(“模块名.函数名”)
3.2 链式函数抛出异常
在rbd_api.py文件中,有一行代码如下:
rbdServ.OpRBD = MagicMock()
rbdServ.OpRBD(pool).side_effect = rados.Error(“Error: error connecting to the cluster: error code 24”)
3.3 全局函数如何mock
例如在文件rbd_api.py中有全局函数checkpoolstat(pool),它是一个全局函数,这样在进行单元测试的过程中,我们可能需要mock该函数。该函数的具体代码如下:
因此,我们在test_rbd_api.py文件中为该函数撰写单元测试,可以这么做。 在文件开始处导入该rbd_api模块。
import rbd_api as rbdAPI
def test_patchInvalid_Parameter(self):
……
rbdAPI.checkpoolstat.return_value = MGR_COMMON.POOL_STAT_ERROR
即可。
3.4 链式调用正常
在rbd_api文件中有如下代码行
ret = OpRBD(pool).flatten(img)
在第一个函数未出现异常,在flatten函数中返回值可以在test_rbd_api.py文件中如下写代码:
rbdServ.OpRBD(pool).snap_rollback = MagicMock(return_value = RBD_COMMON.CODE_EXEC_SUCCESS_MODIFY)
3.5 with子句mock
#!/usr/bin/python
import rados
class OpRBD:
def __init__(self):
... def __del__(self):
... def resize(self, img, size):
try:
with rbd.Image(self.ioctx, img) as image:
if image.size() < size:
image.resize(size)
else:
return RBD_COMMON.CODE_ARGUMENT_LESS_THAN_ORIGINAL
except rbd.ImageNotFound as exce1
print(exce1)
return RBD_COMMON.CODE_IMAGE_NOT_FOUND
由于是在with子句中要进行mock,在此简单的对with的知识点进行说明: 要使用 with 语句,首先要明白上下文管理器这一概念。有了上下文管理器,with 语句才能工作。 下面是一组与上下文管理器和with 语句有关的概念。
上下文管理协议(Context Management Protocol):包含方法 enter() 和 exit(),支持 该协议的对象要实现这两个方法。
上下文管理器(Context Manager):支持上下文管理协议的对象,这种对象实现了 enter() 和 exit() 方法。上下文管理器定义执行 with 语句时要建立的运行时上下文, 负责执行 with 语句块上下文中的进入与退出操作。通常使用 with 语句调用上下文管理器,也可以通过直接调用其方法来使用。
运行时上下文(runtime context):由上下文管理器创建,通过上下文管理器的 enter() 和exit() 方法实现,enter() 方法在语句体执行之前进入运行时上下文,exit() 在语句体执行完后从运行时上下文退出。with 语句支持运行时上下文这一概念。
上下文表达式(Context Expression):with 语句中跟在关键字 with 之后的表达式,该表达式要返回一个上下文管理器对象。
语句体(with-body):with 语句包裹起来的代码块,在执行语句体之前会调用上下文管理器的 enter() 方法,执行完语句体之后会执行 exit() 方法。 出现异常时,如果 exit(type, value, traceback) 返回 False,则会重新抛出异常,让with 之外的语句逻辑来处理异常,这也是通用做法;如果返回 True,则忽略异常,不再对异常进行处理。因此,在对with子句进行mock时,要具有两个函数,exit, enter,并且如果在with语句体重抛出异常并被with之外的代码进行捕获异常,要使得exit返回False,因此可以撰写测试代码如下
#!/usr/bin/python
import rados
class OpRBD:
def __init__(self):
...
def __del__(self):
... def resize(self, img, size):
try:
with rbd.Image(self.ioctx, img) as image:
if image.size() < size:
image.resize(size)
else:
return RBD_COMMON.CODE_ARGUMENT_LESS_THAN_ORIGINAL
except rbd.ImageNotFound as exce1
print(exce1)
return RBD_COMMON.CODE_IMAGE_NOT_FOUND
class TestOpRBD(unittest.TestCase):
def setUp(self):
...
def tearDown(self):
...
def test_resize(self):
fake_image = Mock()
fake_image.__enter__ = Mock(return_value = fake_image)
fake_image.__exit__ = Mock(return_value = True)
rbd.Image = Mock(return_value = fake_image)
size = 1073741824L / 2
fake_image.size = Mock(return_value = 1073741824L)
fake_image.resize = Mock(return_value = None)
self.assertEqual(self.opRBD.resize(self.img, size), RBD_COMMON.CODE_ARGUMENT_LESS_THAN_ORIGINAL) size = 2 * 1073741824L
self.assertEqual(self.opRBD.resize(self.img, size), RBD_COMMON.CODE_EXEC_SUCCESS_MODIFY)
rbd.Image = Mock(side_effect = rbd.ImageNotFound("%s image not found!" %self.img))
self.assertEqual(self.resize(self.img, size), RBD_COMMON.CODE_IMAGE_NOT_FOUND)3.6 连续mock
在rbd_api文件中有一个OpRados类的内容如下:
#!/usr/bin/python
import rados class OpRados:
def __init__(self):
self.cluster = rados.Rados(conffile=rconf['conffile'])
self.cluster.connect() def __del__(self):
self.cluster.shutdown() def lists(self):
return util.return_format(RBD_COMMON.CODE_EXEC_SUCCESS_GET, "", self.cluster.list_pools())为该类写单元测试,具体代码如下:
#!/usr/bin/python
import rados
import unittest
from mock import Mock
class TestOpRados(unittest.TestCase):
def setUp(self):
fake_Rados = Mock()
fake_Rados.connect = Mock(return_value = None)
fake_Rados.shutdown = Mock(return_value = None)
fake_Rados.list_pools = Mock(return_value = ["sqh", "sqh1"])
# 注意:此处要使得rados.Rados()调用返回fake_Rados.
# 如果写成rados.Rados = fake_Rados,只能使得self.cluster重新生成一个Mock对象
# 无法有效的控制为fake_Rados所添加的属性。
rados.Rados = Mock(return_value = fake_Rados)
self.opRados = OpRados() def tearDown(self):
fake_Rados = None
self.opRados = None def test_list(self):
return_list = ["sqh", "sqh1"]
self.assertEqual(self.opRados.lists(), util.return_format(RBD_COMMON.CODE_EXEC_SUCCESS_GET, "", return_list))
Mock相关知识和简单应用的更多相关文章
- 进程和cpu的相关知识和简单调优方案
进程就是一段执行的程序,每当一个程序执行时.对于操作系统本身来说,就创建了一个进程,而且分配了相应的资源.进程能够分为3个类别: 1.交互式进程(I/O) 2.批处理进程 (CPU) ...
- 【Python五篇慢慢弹(5)】类的继承案例解析,python相关知识延伸
类的继承案例解析,python相关知识延伸 作者:白宁超 2016年10月10日22:36:57 摘要:继<快速上手学python>一文之后,笔者又将python官方文档认真学习下.官方给 ...
- 移动WEB像素相关知识
了解移动web像素的知识,主要是为了切图时心中有数.本文主要围绕一个问题:怎样根据设备厂商提供的屏幕尺寸和物理像素得到我们切图需要的逻辑像素?围绕这个问题以iphone5为例讲解涉及到的web像素相关 ...
- 【转】java NIO 相关知识
原文地址:http://www.iteye.com/magazines/132-Java-NIO Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的 ...
- 电路相关知识--读<<继电器是如何成为CPU的>>
电路相关知识–读<<继电器是如何成为CPU的>> */--> *///--> *///--> 电路相关知识–读<<继电器是如何成为CPU的> ...
- AJAX跨域调用相关知识-CORS和JSONP(引)
AJAX跨域调用相关知识-CORS和JSONP 1.什么是跨域 跨域问题产生的原因,是由于浏览器的安全机制,JS只能访问与所在页面同一个域(相同协议.域名.端口)的内容. 但是我们项目开发过程中,经常 ...
- 【转载】前端面试“http全过程”将所有HTTP相关知识抛出来了...
原文:前端面试“http全过程”将所有HTTP相关知识抛出来了... 来一篇串通,一个http全过程的问题,把所有HTTP相关知识点都带过一遍 http全过程 输入域名(url)-->DNS映射 ...
- Java 容器相关知识全面总结
Java实用类库提供了一套相当完整的容器来帮助我们解决很多具体问题.因为我本身是一名Android开发者,包括我在内很多安卓开发,最拿手的就是ListView(RecycleView)+BaseAda ...
- HTML入门基础教程相关知识
HTML入门基础教程 html是什么,什么是html通俗解答: html是hypertext markup language的缩写,即超文本标记语言.html是用于创建可从一个平台移植到另一平台的超文 ...
随机推荐
- CF940F Machine Learning(带修莫队)
首先显然应该把数组离散化,然后发现是个带修莫队裸题,但是求mex比较讨厌,怎么办?其实可以这样求:记录每个数出现的次数,以及出现次数的出现次数.至于求mex,直接暴力扫最小的出现次数的出现次数为0的正 ...
- 使用maven打包问题
项目打包:选择项目 右键->run as-> maven install . 项目中使用的是maven项目,将项目打包成war的时候有时候会出现 出现这种情况的时候解决步骤如下: 选择要打 ...
- String 字符串,heredoc,nowdoc
一个字符串可以用 4 种方式表达: 单引号 双引号 heredoc 语法结构 nowdoc 语法结构(自 PHP 5.3.0 起) 单引号 定义一个字符串的最简单的方法是用单引号把它包围起来(字符 ' ...
- 写excel文件-xlsxwriter包的使用
# encoding: utf8 from xlsxwriter.utility import xl_rowcol_to_cell import pandas as pd def df_to_exce ...
- PAT-树-DFS-BFS相关问题解决方案整理
如何建树? 二叉树-建树-方式一 dfs使用root左右指针建立树节点关系,返回根节点root 二叉树-建树-方式二 dfs使用二维数组,int nds[n][2],如:nds[i][0]表示i节点的 ...
- idea使用eclipse风格
说明,只是代码编辑区采用eclipse风格,其他用的是idea的IntelliJ(白色风格) 1.下载文件 2.配置
- 踩一踩win7安装neo4j的坑
本文使用zip解压方式安装,下载社区版zip 解压到喜欢的文件夹,然后配置环境变量NEO4J_HOME=D:\neo4j-community-3.5.5(自己的解压目录) 配置Path=%NEO4J_ ...
- Springboot数据校验
SpringBoot中使用了Hibernate-validate校验框架 1.在实体类中添加校验规则 校验规则: @NotBlank: 判断字符串是否为null或者是空串(去掉首尾空格).@NotEm ...
- RFC文档(http部分)
Request For Comments(RFC),是一系列以编号排定的文件.文件收集了有关互联网相关信息,以及UNIX和互联网社区的软件文件.目前RFC文件是由Internet Society(IS ...
- Ubuntu---Git
本篇文章简单总结了常用 Git 的使用 前言 设置用户信息 1, Git 是分布式的 SSH 代码管理工具,远程的代码管理是基于 SSH 的,所以要使用远程的 Git 则需要 SSH 的配置. ste ...