一.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))

最新文章

  1. forward和redirect的区别(转)
  2. Thinkphp3.2----------------Thinkphp3.2的目录结构介绍
  3. 本机搭建zookeeper集群
  4. iOS AFNetworking “Request failed: unacceptable content-type: text/html”问题
  5. 使用MongoDB的开源项目
  6. 查找字符串(C++实现)
  7. Android 百度地图 SDK v3.0.0 (三) 添加覆盖物Marker与InfoWindow的使用
  8. Qt窗口操作函数(最大化,全屏,隐藏最大化,最小化)
  9. Ubuntu14.04下搭建VPN服务
  10. RabbitMQ In JAVA 介绍及使用
  11. Navicat如何导出Excel格式表结构
  12. php_network_getaddresses: getaddrinfo failed 原因
  13. css3实现水平垂直居中
  14. Python爬虫与数据分析之模块:内置模块、开源模块、自定义模块
  15. font-size:0的妙用,用于解决inline或者inline-block造成的间隙
  16. python+selenium四:iframe查看、定位、切换
  17. 2553 ACM N皇后 回溯递归
  18. Flask学习【第7篇】:Flask中的wtforms使用
  19. MyBatis获取SqlSession
  20. mac下安装ionic

热门文章

  1. The full stack trace of the root cause is available in the Apache Tomcat/8.0.8 logs.
  2. Python说文解字_Python之多任务_05
  3. arp攻击 (可查看同一局域网他人手机照片)
  4. 81.常用的返回QuerySet对象的方法使用详解:values和values_list
  5. 施魔法(DP)
  6. POJ 1258:Agri-Net Prim最小生成树模板题
  7. PHP语言编写的磁力搜索工具下载BT种子 支持transmission、qBittorrent
  8. 猜数字游戏(随机数和Scannner的应用)
  9. ant design for vue select 数据回显问题
  10. LAMP环境搭建,防火墙开启,数据库挂载在逻辑卷