Pytest 接口关联
在接口自动化测试中,接口之间通常存在关联关系,比如前一个接口返回的数据需要传递给后续接口使用。以下是 Pytest 中实现接口关联的方法和详细讲解。
1. 什么是接口关联?
接口关联指的是一个接口的输出作为另一个接口的输入。常见的场景包括:
- 登录接口返回的
token
需要传递给后续的接口。 - 查询接口返回的
ID
用于更新或删除操作。 - 多接口之间的数据依赖,比如依赖创建的资源进行操作。
2. 实现接口关联的常见方法
方法一:使用全局变量
直接使用全局变量存储接口返回值,供其他接口使用。
示例代码:
import pytest import requests # 全局变量 global_data = {} def test_login(): url = "http://example.com/api/login" data = {"username": "user", "password": "pass"} response = requests.post(url, json=data) assert response.status_code == 200 # 提取 token 并存储在全局变量中 global_data["token"] = response.json().get("token") def test_get_user_info(): url = "http://example.com/api/user" headers = {"Authorization": f"Bearer {global_data['token']}"} response = requests.get(url, headers=headers) assert response.status_code == 200
优点:
- 简单易用,适合小型测试场景。
缺点:
- 全局变量可能导致测试数据污染,尤其在并发测试时。
方法二:使用 Fixture
通过 Pytest 的 Fixture 动态传递数据,避免全局变量的污染问题。
示例代码:
import pytest import requests @pytest.fixture def login(): url = "http://example.com/api/login" data = {"username": "user", "password": "pass"} response = requests.post(url, json=data) assert response.status_code == 200 # 返回 token return response.json().get("token") def test_get_user_info(login): url = "http://example.com/api/user" headers = {"Authorization": f"Bearer {login}"} response = requests.get(url, headers=headers) assert response.status_code == 200
优点:
- Fixture 的作用域可以控制数据的生命周期,适合中小型测试。
- 代码结构清晰,易于维护。
缺点:
- Fixture 的嵌套复杂性可能增加。
方法三:上下文管理(Context)
通过上下文类集中管理接口之间的关联数据,适合复杂项目。
示例代码:
import pytest import requests class Context: def __init__(self): self.data = {} def set(self, key, value): self.data[key] = value def get(self, key): return self.data.get(key) @pytest.fixture(scope="session") def context(): return Context() def test_login(context): url = "http://example.com/api/login" data = {"username": "user", "password": "pass"} response = requests.post(url, json=data) assert response.status_code == 200 # 存储 token context.set("token", response.json().get("token")) def test_get_user_info(context): url = "http://example.com/api/user" headers = {"Authorization": f"Bearer {context.get('token')}"} response = requests.get(url, headers=headers) assert response.status_code == 200
优点:
- 数据管理集中化,适合大型测试项目。
- 支持复杂的关联数据。
缺点:
- 代码稍微复杂,需要额外维护上下文类。
方法四:参数化动态传递数据
通过参数化结合接口调用动态传递数据。
示例代码:
import pytest import requests def login(): url = "http://example.com/api/login" data = {"username": "user", "password": "pass"} response = requests.post(url, json=data) assert response.status_code == 200 return response.json().get("token") @pytest.mark.parametrize("token", [login()]) def test_get_user_info(token): url = "http://example.com/api/user" headers = {"Authorization": f"Bearer {token}"} response = requests.get(url, headers=headers) assert response.status_code == 200
优点:
- 简洁,适合测试用例数量少的场景。
缺点:
- 需要提前调用依赖接口,无法动态更新参数。
方法五:测试用例之间共享数据
通过 request.node
实现测试用例之间的数据共享。
示例代码:
import pytest import requests @pytest.fixture(scope="module") def shared_data(request): request.node.shared_data = {} return request.node.shared_data def test_login(shared_data): url = "http://example.com/api/login" data = {"username": "user", "password": "pass"} response = requests.post(url, json=data) assert response.status_code == 200 # 存储 token shared_data["token"] = response.json().get("token") def test_get_user_info(shared_data): url = "http://example.com/api/user" headers = {"Authorization": f"Bearer {shared_data['token']}"} response = requests.get(url, headers=headers) assert response.status_code == 200
优点:
- 测试用例之间可以共享数据。
- 避免全局变量污染。
缺点:
- 不支持多线程并发测试。
3. 各方法适用场景对比
方法 | 适用场景 | 优点 | 缺点 |
---|---|---|---|
全局变量 | 简单的测试场景 | 实现简单快速 | 容易污染全局数据,线程不安全 |
Fixture | 中小型测试项目 | 数据隔离性好,生命周期可控 | 嵌套复杂时维护困难 |
上下文管理 | 大型复杂测试项目 | 数据管理集中化,适合复杂数据共享 | 增加额外代码复杂性 |
参数化传递数据 | 测试用例较少的简单场景 | 代码简洁,适合批量测试 | 无法动态更新 |
用例之间共享数据 | 依赖较强的测试用例 | 避免全局变量污染,模块化强 | 不适合并发测试 |