在接口测试中,使用 pytest 和 yaml 文件结合可以方便地实现参数化测试、接口参数关联以及数据驱动。以下是一个关于接口参数关联和使用 yaml 文件编写用例的详细介绍与示例。


目标

  1. 接口参数关联:将一个接口的返回值作为另一个接口的入参。
  2. 使用 YAML 文件:通过 YAML 文件管理测试用例,方便维护和扩展。
  3. 实现代码简洁、灵活的接口测试框架。

实现步骤

1. 准备工作

  • 安装依赖:

    pip install pytest pyyaml requests
    
  • 示例场景:

    • 接口 A:用户登录,返回 token。
    • 接口 B:获取用户信息,需要传入 token。

2. YAML 文件编写测试用例

我们使用 YAML 文件来管理接口测试数据。以下是一个示例的 testcases.yml 文件:

# testcases.yml
login:
  url: "http://example.com/api/login"
  method: "POST"
  headers:
    Content-Type: "application/json"
  data:
    username: "test_user"
    password: "test_pass"
  extract: "token"  # 从响应中提取 token

get_user:
  url: "http://example.com/api/user"
  method: "GET"
  headers:
    Authorization: "Bearer ${token}"  # 使用 token 作为参数

3. 编写测试代码

以下是结合 pytest 和 yaml 文件的测试代码:

import pytest
import yaml
import requests


@pytest.fixture
def load_test_cases():
    """加载 YAML 文件中的测试用例"""
    with open("testcases.yml", "r", encoding="utf-8") as file:
        test_cases = yaml.safe_load(file)
    return test_cases


def extract_value(response, key):
    """从响应中提取指定字段的值"""
    try:
        return response.json().get(key)
    except Exception as e:
        raise ValueError(f"无法从响应中提取键 '{key}': {e}")


@pytest.fixture
def login_token(load_test_cases):
    """登录接口,获取 token"""
    login_case = load_test_cases["login"]

    # 请求登录接口
    response = requests.request(
        method=login_case["method"],
        url=login_case["url"],
        headers=login_case["headers"],
        json=login_case["data"],
    )

    # 提取 token
    if response.status_code == 200:
        token = extract_value(response, login_case["extract"])
        if token:
            return token
        else:
            raise ValueError("未能从登录响应中提取 token")
    else:
        raise AssertionError(f"登录失败,状态码: {response.status_code}, 响应: {response.text}")


def test_get_user_info(load_test_cases, login_token):
    """测试获取用户信息接口"""
    user_case = load_test_cases["get_user"]

    # 替换参数中的 token
    headers = user_case["headers"]
    headers["Authorization"] = headers["Authorization"].replace("${token}", login_token)

    # 请求用户信息接口
    response = requests.request(
        method=user_case["method"],
        url=user_case["url"],
        headers=headers,
    )

    # 断言
    assert response.status_code == 200, f"获取用户信息失败: {response.text}"
    assert "user" in response.json(), "响应中未包含用户信息"

4. 代码详解

  1. load_test_cases Fixture:

    • 负责加载 testcases.yml 文件中的测试用例数据。
  2. login_token Fixture:

    • 调用登录接口,提取返回的 token 并作为后续接口的参数。
  3. extract_value 函数:

    • 用于从接口响应中提取指定的字段值(如 token),支持灵活扩展。
  4. 接口参数关联:

    • 在 testcases.yml 文件中,Authorization 字段的值使用占位符 ${token},测试代码会动态替换为实际的 token。
  5. 测试用例的执行:

    • test_get_user_info 测试用户信息接口时,通过 login_token 自动获取登录后返回的 token。

5. 优化建议

  • 分层设计:

    • 将请求发送、参数化处理、结果校验分离,便于维护。
  • 添加更多断言:

    • 校验响应时间、响应字段等,确保接口的可靠性。
  • 支持多用例参数化:

    • 在 YAML 文件中定义多个用例,使用 pytest.mark.parametrize 实现批量测试。

6. 支持多用例参数化

改进后的 testcases.yml:

testcases:
  - name: "测试获取用户信息成功"
    login:
      url: "http://example.com/api/login"
      method: "POST"
      headers:
        Content-Type: "application/json"
      data:
        username: "test_user"
        password: "test_pass"
      extract: "token"
    get_user:
      url: "http://example.com/api/user"
      method: "GET"
      headers:
        Authorization: "Bearer ${token}"
    expected_status: 200
    expected_key: "user"
  - name: "测试无效 token"
    login:
      url: "http://example.com/api/login"
      method: "POST"
      headers:
        Content-Type: "application/json"
      data:
        username: "test_user"
        password: "wrong_pass"
      extract: "token"
    get_user:
      url: "http://example.com/api/user"
      method: "GET"
      headers:
        Authorization: "Bearer invalid_token"
    expected_status: 401
    expected_key: null

对应的测试代码:

@pytest.mark.parametrize("case", load_test_cases()["testcases"])
def test_api(case):
    """测试接口"""
    # 登录接口
    response = requests.request(
        method=case["login"]["method"],
        url=case["login"]["url"],
        headers=case["login"]["headers"],
        json=case["login"]["data"],
    )

    # 提取 token
    token = extract_value(response, case["login"]["extract"]) if response.status_code == 200 else None

    # 替换 token
    headers = case["get_user"]["headers"]
    headers["Authorization"] = headers["Authorization"].replace("${token}", token or "invalid_token")

    # 请求用户信息接口
    response = requests.request(
        method=case["get_user"]["method"],
        url=case["get_user"]["url"],
        headers=headers,
    )

    # 断言
    assert response.status_code == case["expected_status"], f"状态码不匹配: {response.text}"
    if case["expected_key"]:
        assert case["expected_key"] in response.json(), f"响应中未找到键: {case['expected_key']}"

总结

  • 使用 pytest 和 yaml 文件可以实现高效的参数化接口测试。
  • 接口参数关联通过提取关键字段(如 token)实现。
  • YAML 文件的使用让用例数据和测试逻辑分离,便于维护和扩展。

更多推荐