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

共 4015字,需浏览 9分钟

 ·

2021-02-06 08:52


↑ 关注 + 星标 ,每天学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 方案进行了讲解,实际应用中,建议根据项目实际情况进行选型。

见面礼


码加我微信备注「三剑客」送你上图三本Python入门电子书 


推荐阅读


  1. 熬夜一周整理我的数据分析学习资源

  2. 为什么建议大家使用 Linux 开发?爽(外加七个感叹号)

  3. 我为什么抛弃Windows,入坑MacBook

  4. 自学Python3年,我终于做了这个决定....

点分享
点收藏
点点赞
点在看
浏览 41
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报