在使用 pytest 进行单元测试时,经常会遇到需要模拟(mock)外部服务或函数的情况。unittest.mock 是 Python 标准库中的一个模块,提供了强大的 mocking 功能。结合 pytest,可以很方便地进行单元测试。本文将详细介绍如何在 pytest 中封装和调用 mock 服务。

如何在 pytest 中封装和调用 Mock 服务

1.安装依赖

确保你已经安装了 pytest。unittest.mock 是 Python 标准库的一部分,所以你不需要额外安装它。

pip install pytest

 

  1. 创建被测试的模块

假设我们有一个模块 my_module.py,其中有一个函数 fetch_data,它调用了一个外部 API 来获取数据。

# my_module.py
import requests
def fetch_data(url):
    """
    从给定的 URL 获取数据。
    参数:
        url (str): API 端点的 URL。
    返回:
        dict: API 返回的 JSON 数据。
    异常:
        requests.exceptions.HTTPError: 如果 HTTP 请求失败。
    """
    response = requests.get(url)
    response.raise_for_status()  # 如果 HTTP 请求失败,抛出异常
    return response.json()

 

3.创建测试文件

创建一个测试文件 test_my_module.py,使用 pytest 和 unittest.mock 来编写测试用例。

3.1 导入必要的模块

# test_my_module.py
import pytest
from unittest.mock import MagicMock, patch
from my_module import fetch_data

 

3.2 定义一个 fixture 来设置 mock 对象

fixture 是 pytest 提供的一个特性,用于设置测试环境。我们可以使用 fixture 来模拟 requests.get 方法。

# 定义一个 fixture 来设置 mock 对象
@pytest.fixture
def mock_requests_get():
    """
    使用 pytest 的 fixture 来模拟 requests.get 方法。
    返回:
        MagicMock: 模拟的 requests.get 对象。
    """
    with patch('requests.get') as mock_get:
        yield mock_get

 

3.3 封装 mock 行为

为了简化测试用例,我们可以将常见的 mock 行为封装成一个函数。

# 封装 mock 行为
def setup_mock_response(mock_get, status_code=200, json_data=None):
    """
    设置 mock 响应。
    参数:
        mock_get (MagicMock): 模拟的 requests.get 对象。
        status_code (int): 响应的状态码,默认为 200。
        json_data (dict): 响应的 JSON 数据,默认为 None。
    """
    # 创建一个模拟的响应对象
    mock_response = MagicMock()
    mock_response.status_code = status_code  # 设置状态码
    if json_data is not None:
        mock_response.json.return_value = json_data  # 设置 JSON 响应数据
    if status_code >= 400:
        mock_response.raise_for_status.side_effect = requests.exceptions.HTTPError("请求失败")  # 设置异常
    # 设置 mock_get 的返回值为模拟的响应对象
    mock_get.return_value = mock_response

 

3.4 编写测试用例

现在我们可以编写具体的测试用例了。

# 测试用例:成功的请求
def test_fetch_data_success(mock_requests_get):
    """
    测试 fetch_data 函数在成功情况下的行为。
    """
    # 设置 mock 响应
    setup_mock_response(mock_requests_get, status_code=200, json_data={"key": "value"})
    # 调用被测试的函数
    result = fetch_data("http://example.com/api/data")
    # 断言返回值是否正确
    assert result == {"key": "value"}
    # 断言 requests.get 是否被正确调用
    mock_requests_get.assert_called_once_with("http://example.com/api/data")
# 测试用例:失败的请求
def test_fetch_data_failure(mock_requests_get):
    """
    测试 fetch_data 函数在失败情况下的行为。
    """
    # 设置 mock 响应
    setup_mock_response(mock_requests_get, status_code=404)
    # 调用被测试的函数,并期望抛出异常
    with pytest.raises(requests.exceptions.HTTPError):
        fetch_data("http://example.com/api/data")
    # 断言 requests.get 是否被正确调用
    mock_requests_get.assert_called_once_with("http://example.com/api/data")

 

  1. 运行测试

在终端中运行以下命令来执行测试:

pytest test_my_module.py

解释

安装依赖:

使用 pip install pytest 安装 pytest,unittest.mock 已经包含在 Python 标准库中。

创建被测试的模块:

my_module.py 中的 fetch_data 函数从给定的 URL 获取数据,并返回 JSON 响应。如果请求失败,会抛出 HTTPError 异常。

创建测试文件:

test_my_module.py 中的 mock_requests_get fixture 使用 patch 装饰器来模拟 requests.get 方法。

setup_mock_response 函数用于设置模拟的响应对象,包括状态码和 JSON 数据。

test_fetch_data_success 测试用例验证 fetch_data 在成功情况下的行为。

test_fetch_data_failure 测试用例验证 fetch_data 在失败情况下的行为,期望抛出 HTTPError 异常。

如何在 Python 中使用 unittest.mock 来封装和调用 Mock 服务?

1.创建被测试的模块

假设我们有一个模块 my_module.py,其中有一个函数 fetch_data,它调用了一个外部 API 来获取数据。

# my_module.py
import requests
def fetch_data(url):
    """
    从给定的 URL 获取数据。
    参数:
        url (str): API 端点的 URL。
    返回:
        dict: API 返回的 JSON 数据。
    异常:
        requests.exceptions.HTTPError: 如果 HTTP 请求失败。
    """
    response = requests.get(url)
    response.raise_for_status()  # 如果 HTTP 请求失败,抛出异常
    return response.json()

 

2.创建测试文件

创建一个测试文件 test_my_module.py,使用 pytest 和 unittest.mock 来编写测试用例。

2.1 导入必要的模块

# test_my_module.py
import pytest
from unittest.mock import MagicMock, patch
from my_module import fetch_data

 

2.2 定义一个 fixture 来设置 mock 对象

fixture 是 pytest 提供的一个特性,用于设置测试环境。我们可以使用 fixture 来模拟 requests.get 方法。

# 定义一个 fixture 来设置 mock 对象
@pytest.fixture
def mock_requests_get():
    """
    使用 pytest 的 fixture 来模拟 requests.get 方法。
    返回:
        MagicMock: 模拟的 requests.get 对象。
    """
    with patch('requests.get') as mock_get:
        yield mock_get

 

2.3 封装 mock 行为

为了简化测试用例,我们可以将常见的 mock 行为封装成一个函数。

# 封装 mock 行为
def setup_mock_response(mock_get, status_code=200, json_data=None):
    """
    设置 mock 响应。
    参数:
        mock_get (MagicMock): 模拟的 requests.get 对象。
        status_code (int): 响应的状态码,默认为 200。
        json_data (dict): 响应的 JSON 数据,默认为 None。
    """
    # 创建一个模拟的响应对象
    mock_response = MagicMock()
    mock_response.status_code = status_code  # 设置状态码
    if json_data is not None:
        mock_response.json.return_value = json_data  # 设置 JSON 响应数据
    if status_code >= 400:
        mock_response.raise_for_status.side_effect = requests.exceptions.HTTPError("请求失败")  # 设置异常
    # 设置 mock_get 的返回值为模拟的响应对象
    mock_get.return_value = mock_response

 

2.4 编写测试用例

现在我们可以编写具体的测试用例了。

# 测试用例:成功的请求
def test_fetch_data_success(mock_requests_get):
    """
    测试 fetch_data 函数在成功情况下的行为。
    """
    # 设置 mock 响应
    setup_mock_response(mock_requests_get, status_code=200, json_data={"key": "value"})
    # 调用被测试的函数
    result = fetch_data("http://example.com/api/data")
    # 断言返回值是否正确
    assert result == {"key": "value"}
    # 断言 requests.get 是否被正确调用
    mock_requests_get.assert_called_once_with("http://example.com/api/data")
# 测试用例:失败的请求
def test_fetch_data_failure(mock_requests_get):
    """
    测试 fetch_data 函数在失败情况下的行为。
    """
    # 设置 mock 响应
    setup_mock_response(mock_requests_get, status_code=404)
    # 调用被测试的函数,并期望抛出异常
    with pytest.raises(requests.exceptions.HTTPError):
        fetch_data("http://example.com/api/data")
    # 断言 requests.get 是否被正确调用
    mock_requests_get.assert_called_once_with("http://example.com/api/data")

 

3.运行测试

在终端中运行以下命令来执行测试:

pytest test_my_module.py

 

解释

安装依赖:

使用 pip install pytest 安装 pytest,unittest.mock 已经包含在 Python 标准库中。

创建被测试的模块:

my_module.py 中的 fetch_data 函数从给定的 URL 获取数据,并返回 JSON 响应。如果请求失败,会抛出 HTTPError 异常。

创建测试文件:

test_my_module.py 中的 mock_requests_get fixture 使用 patch 装饰器来模拟 requests.get 方法。

setup_mock_response 函数用于设置模拟的响应对象,包括状态码和 JSON 数据。

test_fetch_data_success 测试用例验证 fetch_data 在成功情况下的行为。

test_fetch_data_failure 测试用例验证 fetch_data 在失败情况下的行为,期望抛出 HTTPError 异常。

详细说明

patch 装饰器:

patch 装饰器用于在测试过程中临时替换指定的对象。例如,@patch(‘requests.get’) 会临时替换 requests.get 方法,使其返回一个模拟的对象。

MagicMock:

MagicMock 是 unittest.mock 模块中的一个类,用于创建模拟对象。它可以自动记录调用历史,并支持各种方法和属性的模拟。

setup_mock_response 函数:

这个函数用于设置模拟的响应对象,包括状态码和 JSON 数据。通过传入不同的参数,可以模拟不同的响应情况。

assert_called_once_with:

这个方法用于断言某个方法是否被调用过一次,并且传入的参数是否正确。

通过这些步骤,你可以轻松地使用 pytest 和 unittest.mock 来编写和运行单元测试。希望这个示例对你有帮助!如果有任何问题或需要进一步的帮助,请随时告诉我。🌟

感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取   

更多推荐