在测试交付的紧张节奏下,差异代码覆盖率成为测试工程师绕不开的难题。而“DeepSeek”这种差异代码分析技术与 HAR 包结合的方式,则为高效生成接口测试用例提供了新思路。通过解析 HAR 包并结合差异代码分析工具(如阿里百炼 API),可以快速生成接口测试用例并结合 pytest 框架执行,覆盖绝大部分差异代码。

本文将从HAR 包解析接口用例生成pytest 执行,为测试工程师提供一套完整、可落地的解决方案,并附带实战代码!想获取完整的接口测试框架源码?记得关注文末公众号获取哦!


背景与问题

测试工程师的日常难题:

  • 差异代码覆盖率低:新版本交付时,差异代码无法快速覆盖,存在漏测风险。
  • 测试用例编写耗时:从零设计接口用例效率低,尤其在时间紧迫的交付场景下。
  • 缺乏工具支撑:缺少自动化工具,无法快速生成用例覆盖差异代码。

解决方案思路:

  1. 利用 HAR 包(HTTP Archive)记录接口调用数据,快速生成基础接口测试用例。
  2. 结合 阿里百炼 API 提取差异代码对应的接口列表,补充未覆盖的接口测试用例。
  3. 使用 pytest 框架 执行用例,验证差异代码的功能,实现快速覆盖交付。

解决方案分步实现

1. HAR 包解析并生成基础接口用例

1.1 不使用接口测试框架

HAR 包是浏览器或抓包工具(如 Chrome DevTools、Fiddler、Postman)生成的 HTTP 请求记录文件,包含完整的接口调用信息,包括 URL、请求方法、Header、Body 等。

示例代码:解析 HAR 并生成 pytest 用例

以下代码解析 HAR 文件,将接口请求信息转化为 pytest 可执行的测试用例。这样就可以直接执行这些用例,只需更换请求头里的鉴权信息。在无接口用例框架的情况下也不失为一种好的方法。

import json
from typing import List

def parse_har_to_pytest(har_file: str) -> List[dict]:
    """
    解析 HAR 文件并生成 pytest 测试用例
    """
    with open(har_file, "r", encoding="utf-8") as f:
        har_data = json.load(f)

    test_cases = []
    for entry in har_data["log"]["entries"]:
        request = entry["request"]
        method = request["method"]
        url = request["url"]
        headers = {header["name"]: header["value"] for header in request["headers"]}
        body = request.get("postData", {}).get("text", None)

        test_cases.append({
            "method": method,
            "url": url,
            "headers": headers,
            "body": body
        })

    return test_cases


def generate_pytest_file(test_cases: List[dict], output_file: str):
    """
    根据解析的测试用例生成 pytest 文件
    """
    with open(output_file, "w", encoding="utf-8") as f:
        f.write("import pytest\nimport requests\n\n")
        for i, case in enumerate(test_cases):
            f.write(f"def test_case_{i}():\n")
            f.write(f"    url = '{case['url']}'\n")
            f.write(f"    method = '{case['method']}'\n")
            f.write(f"    headers = {case['headers']}\n")
            f.write(f"    body = {repr(case['body'])}\n")
            f.write(f"    response = requests.request(method, url, headers=headers, data=body)\n")
            f.write(f"    assert response.status_code == 200\n\n")


# 示例调用
har_file = "example.har"  # HAR 文件路径
pytest_file = "test_generated.py"  # 输出的 pytest 测试文件路径

test_cases = parse_har_to_pytest(har_file)
generate_pytest_file(test_cases, pytest_file)
print(f"pytest 测试文件生成成功:{pytest_file}")

功能解析:

  • parse_har_to_pytest:解析 HAR 文件,提取接口的 URL、方法、Headers 和 Body,生成标准化的用例数据结构。
  • generate_pytest_file:将用例数据结构转化为 pytest 测试文件,每个接口生成一个测试函数。

执行结果:
生成的 test_generated.py 文件可直接使用 pytest 执行:

pytest test_generated.py
1.2 使用接口测试框架

使用接口测试框架,则可将har解析为兼容接口用例框架的yml文件。

示例代码:解析 HAR 并生成兼容接口测试框架的yml文件
import json
from utils.yml_utils import YamlUtil
from urllib.parse import urlparse, unquote
import re

get_case_list = []
post_case_list = []

with open('har包路径.har', encoding='utf-8') as f:
    content = json.loads(f.read())
    log_content = content['log']
    entries_content = log_content['entries']
    # 这里是要过滤掉的api
    filter_url_keyword = [
                          '/shopping/cart/num',
                          '/instances/count',
                          '.css',
                          '.js',
                          ]
    for entries in entries_content:
        case_info = {}
        filter_flag = False  # 过滤标志位
        request_content = entries['request']
        method = request_content['method'].upper()
        url_ = request_content['url']
        for keyword in filter_url_keyword:
            if keyword in url_:
                filter_flag = True
                break
        if filter_flag:
            continue
        if method == 'GET':
            url_ = url_.split('?')[0]
            # case_info['headers'] = request_content['headers']
            query_string = request_content['queryString']
            params = {}
            for query_param in query_string:
                new_key = None
                new_value = None
                for key, value in query_param.items():
                    if key == 'name':
                        new_key = value
                    elif key == 'value':
                        value = unquote(value)
                        new_value = value
                params[new_key] = new_value
            case_info['params'] = params
        elif method == 'POST':
            headers = request_content['headers']
            for header in headers:
                if header['name'] == 'Content-Type':
                    case_info['Content-Type'] = header['value']
            request_data = request_content.get('postData', None)
            if request_data:
                request_data = request_data.get('text', '')
                if not request_data:
                    params = {}
                else:
                    try:
                        if 'application/json' in request_data:
                            request_data = json.loads(request_data)
                            if isinstance(request_data, dict):
                                params = request_data.get('text', '')
                        elif 'form-data' in request_data:
                            # 利用正则表达式提取参数及其值
                            pattern = r'Content-Disposition: form-data; name="([^"]+)"\s+([^------]+)'
                            matches = re.findall(pattern, request_data, re.DOTALL)

                            # 创建字典以存储参数
                            params_dict = {key: value.strip() for key, value in matches}

                            # 转换为 JSON 字符串
                            json_result = json.dumps(params_dict, ensure_ascii=False)
                            params = json.loads(json_result)
                    except Exception as e:
                        print(type(request_data))
                        print(request_data)
            else:
                params = {}
            case_info['params'] = params
        response_content = entries['response']
        # print(response_content)
        response_content_dict = response_content.get('content', None)
        if response_content_dict:
            response_text = json.loads(response_content_dict.get('text', None))
            if response_text:
                response_keys = list(response_text.keys())
                if 'message' in response_keys:
                    case_info['validator_key'] = 'message'
                    case_info['validator_value'] = response_text['message']
            else:
                case_info['validator_key'] = None
                case_info['validator_value'] = None
        case_info['can_failed'] = True
        case_info['needed_save_data'] = False
        case_info['needed_replace_data'] = False
        case_info['marks'] = ['regression']
        url_ = url_.replace('http://xxx', '')  # 因接口测试框架中已配置了host,这里手动去掉
        case_info['url'] = url_
        case_info['method'] = method
        case_info['case_title'] = url_
        case_info['needed_save_data_key'] = None
        case_info['needed_save_data_order'] = None
        case_info['tmp_data_file_name'] = None
        case_info['feature'] = url_
        case_info['sub_dir'] = None
        if method == 'GET':
            get_case_list.append(case_info)
        elif method == 'POST':
            post_case_list.append(case_info)

print(get_case_list)
print(post_case_list)
get_case_file_name = 'case_Har包生成的get请求用例.yml'
post_case_file_name = 'case_Har包生成的post请求用例.yml'
YamlUtil(get_case_file_name).write_yaml_1(get_case_list)  # 这里是框架的写入yml文件
YamlUtil(post_case_file_name).write_yaml_1(post_case_list)

功能解析:

  • parse_har_to_pytest:解析 HAR 文件,提取接口的 URL、方法、Headers 和 Body,生成适配接口测试框架的yml文件。效果如下图所示。

在这里插入图片描述


2. 差异代码分析并补充用例

借助 阿里百炼 API 对比两个版本的代码,获取差异文件和对应的接口列表,补充未覆盖的接口测试用例。这里因每个公司的情况不同,具体如何获取需自行处理。

示例代码:调用阿里百炼 API 结合har包生成接口用例
import requests

ALI_BAILIAN_API_URL = "https://api.bailian.aliyun.com/diff"
API_KEY = "your_api_key"  # 替换为实际的 API Key

def get_diff_interfaces(old_version: str, new_version: str):
    """
    获取差异代码对应的接口列表
    """
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {API_KEY}"
    }
    payload = {
        "old_version": old_version,
        "new_version": new_version
    }

    response = requests.post(ALI_BAILIAN_API_URL, headers=headers, json=payload)
    if response.status_code == 200:
        diff_data = response.json()
        print("差异代码分析成功!")
        return diff_data.get("interfaces", [])
    else:
        print(f"差异代码分析失败,状态码:{response.status_code}")
        print(response.text)
        return []

# 示例调用
diff_interfaces = get_diff_interfaces("v1.0.0", "v1.1.0")
print(f"差异接口列表:{diff_interfaces}")

功能解析:

  • 调用阿里百炼 API 获取差异接口列表,可进一步结合 HAR 包解析的基础用例,补充未覆盖的接口。

要通过调用阿里百炼 DeepSeek-R1 提供的推理 API 来解析 HAR 文件中的接口信息,并将这些信息存储在 YML 文件中,接着进行参数化推理以生成全面的接口测试用例,您可以按照以下步骤进行操作:

步骤一:解析 HAR 文件

  1. 获取 HAR 文件

    • 确保您已抓取并保存了 HAR 文件,包含了需要测试的接口请求信息。
  2. 解析 HAR 文件
    使用 Python 中的 haralyzer 库解析 HAR 文件:

    from haralyzer import HarParser, HarPage
    
    # 加载 HAR 文件
    with open('your_file.har', 'r') as har_file:
        har_data = har_file.read()
    
    # 解析 HAR 文件
    har_parser = HarParser(har_data)
    entries = har_parser.har_data['log']['entries']
    
    # 提取接口信息
    api_info = []
    for entry in entries:
        request = entry['request']
        api_info.append({
            'url': request['url'],
            'method': request['method'],
            'headers': request['headers'],
            'postData': request.get('postData', {})
        })
    

步骤二:存储到 YML 文件

  1. 将接口信息存储为 YML
    使用 PyYAML 库将提取的信息写入 YML 文件:
    import yaml
    
    # 存储到 YML 文件
    with open('api_test_cases.yml', 'w') as yaml_file:
        yaml.dump(api_info, yaml_file, default_flow_style=False)
    

步骤三:调用 DeepSeek-R1 推理 API

  1. 准备推理 API 请求
    根据存储的 YML 文件,读取用例并构造 API 请求:
    import requests
    import yaml
    
    # 读取 YML 文件
    with open('api_test_cases.yml', 'r') as yaml_file:
        test_cases = yaml.safe_load(yaml_file)
    
    # 设置请求头
    headers = {
        "Content-Type": "application/json",
        "Authorization": "Bearer <Your Access Token>"
    }
    
    # 调用 DeepSeek-R1 推理 API 进行参数化推理
    for case in test_cases:
        payload = {
            "query": case['url'],
            "parameters": case.get('postData', {}).get('text', {})
        }
    
        response = requests.post("https://api.yourservice.com/deepseek", headers=headers, json=payload)
        print(response.json())
    

步骤四:生成全面的接口测试用例

  1. 生成测试用例
    将返回的推理结果整理为测试用例,可以根据需要自定义每个测试用例的格式。

    test_results = []
    for case, result in zip(test_cases, responses):  # 假设 responses 是收集的所有响应
        test_results.append({
            'url': case['url'],
            'method': case['method'],
            'expected_response': result,  # 根据推理结果设置期望的响应
            'status_code': response.status_code
        })
    
    # 存储生成的测试用例
    with open('generated_test_cases.yml', 'w') as yaml_file:
        yaml.dump(test_results, yaml_file, default_flow_style=False)
    

通过以上步骤,您可以实现对 HAR 文件的解析,将接口信息存储到 YML 文件中,并使用阿里百炼 DeepSeek-R1 的推理 API 进行参数化推理,最终生成覆盖全面的接口测试用例。这一流程不仅提高了测试的效率,还确保了用例的全面性和准确性。

3. pytest 执行与覆盖率验证

生成的 pytest 测试文件可直接运行,并通过覆盖率工具(如 coverage.py)验证差异代码的覆盖情况。

示例代码:pytest 测试执行与覆盖率报告
# 安装 pytest 和 coverage
pip install pytest coverage

# 执行用例并生成覆盖率报告
coverage run -m pytest test_generated.py
coverage html

执行后会生成 htmlcov/index.html,可通过浏览器查看覆盖率报告。


方案优势

  1. 效率高:HAR 包解析结合差异代码分析,快速生成接口测试用例,节省 80% 的用例设计时间。
  2. 差异覆盖率高:阿里百炼 API 精确定位差异代码,与实际接口调用结合,覆盖率显著提升。
  3. 自动化强:结合 pytest 框架自动化执行,无需手动干预。如果不结合已有的接口测试框架,则需要手动维护接口的鉴权信息,执行用例前需更新token或cookie等信息。

总结

通过 HAR 包解析与差异代码分析相结合,测试工程师可以高效生成接口测试用例并快速覆盖差异代码。本方案依托阿里百炼 API 和 pytest 框架,具有极强的落地实践性,是测试工程师提升效率、解决差异覆盖的利器。如果你正在为覆盖率发愁,不妨试试这个方法!

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐