如何在 pytest 单元测试中封装和调用 Mock 服务
如何在 pytest 中封装和调用 Mock 服务
pip install pytest
2. 创建被测试的模块
假设我们有一个模块 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 对象
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")
4. 运行测试
在终端中运行以下命令来执行测试:
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 异常。
# 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 对象
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:
这个方法用于断言某个方法是否被调用过一次,并且传入的参数是否正确。
