社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

Python 中 Mock 到底该怎么玩?一篇文章告诉你(超全)

Python编程时光 • 4 年前 • 569 次点击  

点击上方“Python编程时光”,选择“加为星标

第一时间关注 Python 原创干货!


1. 前言

微服务架构下,由于各类服务开发进度的不一致,导致联调工作经常会存在不确定性,进而导致项目延期

在实际工作中,为了保证项目进度,我们经常需要针对部分未完成模块及不稳定模块采用 Mock 方式,以验证已开发完的模块

本篇文章将介绍 Python 实现 Mock 的几种常见方式

2. Mock 介绍

Mock 测试:在测试验证过程中,对于那些尚未完成或不稳定的对象,用一个虚拟对象来替代,以便测试的测试方法

因此,这个虚拟的对象是 Mock 对象,Mock 对象是真实对象在调试期间的代替品

它的优势包含:

  • 前、后端并行开发

  • 模拟无法访问的资源

  • 隔离系统,避免脏数据干扰测试结果

3.1 mock

在 Python 3.3 之前使用 mock,需要先安装依赖

# 安装mock依赖
pip3 install mock

项目地址:

https://github.com/testing-cabal/mock

假设 Product 类中有 2 个方法

  • get_product_status_by_id

  • buy_product

其中,get_product_status_by_id 方法还没有实现;buy_product 方法依赖于 get_product_status_by_id 方法的返回值
# product_impl.py

class Product(object):

    def __init__(self):
        pass

    def get_product_status_by_id(self, product_id):
        """
        通过商品id获取产品信息(Mock)
        :return:
        """

        # 待实现查询数据库的业务逻辑
        pass

    def buy_product(self, product_id):
        """
        购买产品(真实逻辑)
        :return:
        """

        # 产品信息
        # {"id":1,"name":"苹果","num":23}
        product = self.get_product_status_by_id(product_id)

        if product.get("num") >= 1:
            result = {"status"0"msg""购买成功!"}
        else:
            result = {"status"1"msg""购买失败,库存不足!"}

        return result
Mock 的步骤如下:
  • 导入使用 mock 中的 patch 方法

  • 作为测试方法的装饰器,对 get_product_status_by_id 方法进行 Mock,方法参数为 Mock 对象

  • 测试方法中,对该 Mock 对象设置一个返回值

  • 调用并断言

from mock import patch
from mock_.product_impl import Product

@patch('mock_.product_impl.Product.get_product_status_by_id')
def test_succuse(mock_get_product_status_by_id):
    # Mock方法,指定一个返回值
    mock_get_product_status_by_id.return_value = {"id"1"name""苹果""num"23}

    product = Product()

    assert product.buy_product(1).get("status") == 0

需要注意的是,Mock 此方法的时候,必须制定该方法的完整路径

使用 @patch.object 同样能完成 Mock,不同的是,@patch.object 包含 2 个参数

第一个参数为该方法所在的类;第二个参数为方法名

from mock import patch

from  mock_.product_impl import Product

# Mock一个方法
# @patch.object:对象、方法名
@patch.object(Product, 'get_product_status_by_id')
def test_succuse(mock_get_product_status_by_id):
    # Mock方法,指定一个返回值
    mock_get_product_status_by_id.return_value = {"id"1"name""苹果""num"23}

    product = Product()

    assert product.buy_product(1).get("status") == 0

3.2 unittest.mock

Python 3.3 之后,mock 作为标准库,已经内置到 unittest 中了

还是以 3.1 的场景为例,使用 unittest 编写一个测试用例

Mock 步骤如下:

  • 导入 unittest 框架中的 mock 文件

  • 例化 Product 对象

  • mock.Mock(return_value=*) 方法

    get_product_status_by_id 方法进行 Mock

  • 调用并断言

import unittest
from unittest import mock

from unittest_mock.product_impl import Product

class TestProduct(unittest.TestCase):

    def test_success(self):
        # 成功结果
        mock_success_value = {"id"1"name""苹果""num"23}

        product = Product()

        product.get_product_status_by_id = mock.Mock(return_value=mock_success_value)

        # 调用实际函数
        assert product.buy_product(1).get("status") == 0

if __name__ == "__main__":
    unittest.main()

3.3 pytest.mock

相比 unittest,pytest 由于强大的插件支持,用户群体可能更大!

如果项目本身使用的框架是 pytest,则 Mock 更建议使用 pytest-mock 这个插件

# pytest依赖
pip3 install pytest

Mock 步骤如下:

  • 使用 pytest 编写测试方法,参数为 mocker

  • 例化 Product 对象

  • 使用 mocker.patch() 方法对 get_product_status_by_id 方法进行 Mock,并设置返回值

  • 调用并断言

import pytest

from pytest_mock_.product_impl import Product

def test_buy_product_success(mocker):
    """
    购买成功Mock
    :param mocker:
    :return:
    """

    # 实例化一个产品对象
    product = Product()

    # 对Product中的方法的返回值进行Mock
    mock_value = {"id"1"name""苹果" "num"23}

    # Mock方法
    # 注意:需要指定方法的完整路径
    # mocker.patch 的第一个参数必须是模拟对象的具体路径,第二个参数用来指定返回值
    product.get_product_status_by_id = mocker.patch("product_impl.Product.get_product_status_by_id",
                                                    return_value=mock_value)

    # 调用购买产品的方法
    result = product.buy_product(1)

    assert result.get("status") == 0

需要注意的是,mocker.patch 方法第一个参数必须是 Mock 对象的完整路径

4. 最后

文中对 Python 中常见的 Mock 方案进行了讲解,实际应用中,建议根据项目实际情况进行选型。


- EOF -

推荐阅读  点击标题可跳转
醒醒吧!Python 很早就支持中文变量名了
别再问我怎么Python打包成exe了
别瞎学了,这几门语言要被淘汰了!
太棒了!Jupyter 与PyCharm 完美融合,Jupytext 来啦!
一学就会的 Python 时间转化总结(超全)



得本文对你有帮助?请分享给更多人
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/106531
 
569 次点击