• 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 一个类?

            有时候须要模拟一个函数或者类的行为。包含它全部的属性和方法,假设手动去一个个加入,实在低效并且easy出错。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'])

  • 相关阅读:
    129 01 Android 零基础入门 02 Java面向对象 06 Java单例模式 03 饿汉模式 VS 懒汉模式 02 懒汉式的代码实现
    128 01 Android 零基础入门 02 Java面向对象 06 Java单例模式 03 饿汉模式 VS 懒汉模式 01 饿汉式的代码实现
    127 01 Android 零基础入门 02 Java面向对象 06 Java单例模式 02 单例模式概述 01 单例模式的定义和作用
    126 01 Android 零基础入门 02 Java面向对象 06 Java单例模式 01 设计模式概述 01 设计模式简介
    125 01 Android 零基础入门 02 Java面向对象 05 Java继承(下)05 Java继承(下)总结 01 Java继承(下)知识点总结
    leetcode-----121. 买卖股票的最佳时机
    leetcode-----104. 二叉树的最大深度
    Json串的字段如果和类中字段不一致,如何映射、转换?
    Mybatis-Plus的Service方法使用 之 泛型方法default <V> List<V> listObjs(Function<? super Object, V> mapper)
    模糊查询
  • 原文地址:https://www.cnblogs.com/blfshiye/p/5255317.html
Copyright © 2020-2023  润新知