在接口自动化测试中,接口之间通常存在关联关系,比如前一个接口返回的数据需要传递给后续接口使用。以下是 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 | 中小型测试项目 | 数据隔离性好,生命周期可控 | 嵌套复杂时维护困难 |
上下文管理 | 大型复杂测试项目 | 数据管理集中化,适合复杂数据共享 | 增加额外代码复杂性 |
参数化传递数据 | 测试用例较少的简单场景 | 代码简洁,适合批量测试 | 无法动态更新 |
用例之间共享数据 | 依赖较强的测试用例 | 避免全局变量污染,模块化强 | 不适合并发测试 |