python第三方库系列之十九--python测试使用的mock库

一、为什么需要mock

在写unittest的时候,如果系统中有很多外部依赖,我们不需要也不希望把所有的部件都运行一遍。比如,要验证分享到微博的功能,如果每次测试的时候都要真实地把接口调用一遍,不仅效率低,制造很多垃圾数据,还可能因为外部因素导致unittest失败。对于有些耗时更久,或者无法简单创建测试环境的系统,真实的测试就显得更不必要。

我们只需要知道代码按照预期执行,并调用了相关的外部接口。还是拿分享到微博这个功能做例子,分享部分的伪代码可能是这样的:

def share():
    """Share system generated message to weibo."""
    msg = generate_msg()
    weibo = get_weibo_client(user_id)
    weibo.upload(msg)

如果有一种方法,测试上面代码的时候能够运行所有的代码,但是并不实际执行weibo.upload(msg),而且还能知道每个函数被调用了几次,每次被调用的参数,那我们测试用例就方便多了。

python中mock就是在测试的时候用来模拟外部服务的,一般下面的场景会使用到mock:

a.数据库操作:没有必要每一次都去读写数据库

b.HTTP 请求:网络操作很耗时,测试的时候还要依赖外部的服务

c.外部命令:执行系统命令,比如文件操作,进程操作等等。

二、mock的基本原理

上面也提到过,mock是替换代码中外部的服务。因为python是动态语言,一切都是对象,所以在执行之前把实例、方法、函数和变量替换掉。比如:

>>> import os

>>> def myremove(filename):
>>>     return filename

>>> os.remove = myremove
<function __main__.myremove>

>>> print os.remove('test-file')
test-file

上面的例子是最简单的说明,如果把myremove修改成Mock类,然后这个类里面在调用的时候(复写 __call__)能够根据传进来的参数决定它的行为,还能记录每一次调用,你就大致了解 Mock 做了什么。

三、mock的使用

(1)怎么 mock 一个函数?

from mock import Mock

myMethod = Mock()
myMethod.return_value = 3
myMethod(1, 'a', foo='bar')

myMethod.assert_called_with(1, 'a', foo='bar')    # True
myMethod()
myMethod.call_count                               # 2

想要mock出一个函数,直接使用mock.Mock()实例,你可以在初始化的时候设定返回值myMethod = Mock(return_value=3),也可以通过myMethod.return_value的属性来设置。

除了return_value,你还可以mock side_effect,side_effect是一个函数或者异常,在mock的对象被调用的时候会被用同样的参数调用。

myMethod = Mock(side_effect=KeyError('whatever'))
myMethod()

Traceback (most recent call last):
 ...
KeyError: 'whatever'

上面的例子就是模拟一个异常,如果side_effect是函数的话,这个函数就会被调用,可以用来动态地生成返回值。下面的例子mock一个可以返回输入字符串长度的函数。

def side_effect(str):
    return len(str)

myMethod = Mock(side_effect=side_effect)
myMethod('sd')              # 2

在unittest的时候,mock还提供了下面几种assert语句:

assert_any_call

assert_called_once_with

assert_called_with

assert_has_calls

(2)怎么 mock 一个类的方法?

要想mock一个类中的某个方法,可以使用mock提供的patch方法:

import mock
import Module1

@mock.patch.object(Module1.Class1, 'some_method')
def test(mock_method):
    mock_method.return_value = 3
    mock_method.side_effect = some_side_effect
    m = Module1.Class1()
    m.some_method(*args, **kwargs)

    assert m.some_method is mock_method
    m.some_method.assert_called_with(*args, **kwargs)

(3)怎么 mock 一个类?

有时候需要模拟一个函数或者类的行为,包括它所有的属性和方法,如果手动去一个个添加,实在低效而且容易出错。mock提供了autospec的功能,根据提供的模板类生成一个mock实例。 下面是mock一个函数的例子。

import mock

def myFunc(a, b, c):
    pass

>>> mock_func = mock.create_autospec(myFunc, return_value=3)
>>> mock_func(1,2,3)
>>> mock_func.assert_called_with(1,2,3)

>>> mock_func('a string')

Traceback (most recent call last):
 ...
TypeError: <lambda>() takes exactly 3 arguments (1 given)

mock 一个类和这个相同:

>>> mock_class = mock.create_autospec(myClass)

(4)平时的用法

这里用返回值等于3,来模拟requests.post网络交互的返回值。省去了真实的网络交互。当然,也可以用一个方法返回值来取代3这个返回值。

import json
import mock
from django.test import TestCase

class ApiTest(TestCase):

    @mock.patch('apps.agent.requests.post')
    def test(self, mock_method):
        mock_method.return_value = 3
        mock_method.side_effect = some_side_effect
        res = self.client.post('/url/to/post')
        r = json.loads(res.content)
        self.assertEqual(0, r['retval'])

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-05 14:39:07

python第三方库系列之十九--python测试使用的mock库的相关文章

python第三方库系列之十八--python/django test库

django是属于python语音的web框架,要说django測试.也能够先说说python的測试.django能够用python的方式測试,当然,django也基于python封装了一个自己的測试库. 一.python的測试--unitest库 def my_func(a_list, idx): return a_list[idx] import unittest class MyFuncTestCase(unittest.TestCase): def testBasic(self): a

Python之路【第十九篇】:爬虫

Python之路[第十九篇]:爬虫 网络爬虫(又被称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本.另外一些不常使用的名字还有蚂蚁.自动索引.模拟程序或者蠕虫. Requests Python标准库中提供了:urllib.urllib2.httplib等模块以供Http请求,但是,它的 API 太渣了.它是为另一个时代.另一个互联网所创建的.它需要巨量的工作,甚至包括各种方法覆盖,来完成最简单的任务. import

C++语言笔记系列之十九——虚函数(2)

1.构造函数和析构函数中调用虚函数 (1)采用静态编译. (2)在编译阶段自动联接自己类中的函数或基类中的函数,不在派生类中重定义一个函数. 注:构造函数和析构函数中调用的虚函数,派生类都不一定存在的情况下,只能去调用基类或者自身的函数. example 1 #include <iostream.h> class A { public: A() {} virtual void func() {cout<<"A construction."<<endl

python第三方库系列之十四--集群化部署定时任务apscheduler库

如果将定时任务部署在一台服务器上,那么这个定时任务就是整个系统的单点,这台服务器出现故障的话会影响服务.对于可以冗余的任务(重复运行不影响服务),可以部署在多台服务器上,让他们同时执行,这样就可以很简单的避免单点.但是如果任务不允许冗余,最多只能有一台服务器执行任务,那么前面的方法显然行不通.本篇文章就向大家介绍如何避免这种互斥任务的单点问题,最后再介绍一下基于APScheduler的分布式定时任务框架,这个框架是通过多个项目的实践总结而成的. 对于运行在同一台服务器上的两个进程,可以通过加锁实

Python学习笔记系列之000:Python简介

一.Python是什么? Python时候全球4大最流行的编程语言之一,因为其语法简洁.功能强大,目前已广泛应用于人工智能.云计算开发.大数据开发.数据分析.科学运算.网站开发.爬虫.自动化运维.自动化测试.游戏开发金融分析等领域. 二.Python的优点 1. Python的定位是"优雅"."明确"."简单". 2. 开发效率非常高. Python有非常强大的第三方库,基本上你想通过计算机实现任何功能,Python官方库里都有相应的模块进行支持

ComicEnhancerPro 系列教程十九:用JpegQuality看JPG文件的压缩参数

作者:马健邮箱:[email protected] 主页:http://www.comicer.com/stronghorse/ 发布:2017.07.23 教程十九:用JpegQuality看JPG文件的压缩参数 事先声明: 严格说来这篇教程是讲JpegQuality的,其实与CEP本身关系不大,但因为我自己经常从CEP启动JpegQuality查看JPG压缩参数, 我觉得其他人可能也有类似的需求,所以就在CEP系列教程里加了这么一篇,并不是有意给JpegQuality打广告. JpegQua

python第三方库系列之十六--建立最简单的web服务器

利用Python自带的包可以建立简单的web服务器.在DOS里cd到准备做服务器根目录的路径下,输入命令: python -m Web服务器模块 [端口号,默认8000] 例如: python -m SimpleHTTPServer 8080 然后就可以在浏览器中输入 http://localhost:端口号/路径 来访问服务器资源. 例如: http://localhost:8080/index.htm(当然index.htm文件得自己创建) 其他机器也可以通过服务器的IP地址来访问. 这里的

python第三方库系列之十--commands库

我们这次讲的是利用commands模块执行Linux shell命令,当我们用Python写运维脚本时,经常需要执行linux shell的命令,Python中的commands模块专门用于调用Linux shell命令,并返回状态和结果,下面是commands模块的3个主要函数: 1.commands.getoutput('shell command') 2.commands.getstatus('file') 3.commands.getstatusoutput('shell command

python第三方库系列之十五--编码库

首先上一张图: 我们知道:1字节=8位 因为Python的诞生比Unicode标准发布的时间还要早,所以最早的Python只支持ASCII编码,普通的字符串'ABC'在Python内部都是ASCII编码的.Python提供了ord()和chr()函数,可以把字母和对应的数字相互转换: >>> ord('A') 65 >>> chr(65) 'A' Python在后来添加了对Unicode的支持,以Unicode表示的字符串用u'...'表示,比如: >>&g