资讯动态

HAR解析到禅道:接口自动化测试的一键实践

发布时间:2026/10/20 16:39:30 来源:尧图企业网站定制
开工前先聊聊测试工程师的真实困境作为测试工程师每天打交道最多的三件事查接口、记Bug、补用例。可这三件事往往消耗掉一天里大半的精力尤其是遇上版本迭代快的项目一个版本还没测完下一个版本的包又发过来了。手动去梳理一遍接口清单、对照文档补用例再一条条复制请求参数去禅道提Bug每次重复劳动下来脑子是木的手是酸的效率更是低得没眼看。我一直想在“重复劳动”这件事上做点减法。一次偶然的机会我发现浏览器录制的 HAR 文件里实际上藏着一个接口测试的黄金数据源——请求头、请求体、响应状态、时间戳所有信息都齐了。于是花了几天时间写了一套自动化脚本核心逻辑一句话总结把 HAR 文件丢进去一键自动在禅道建好用例遇到失败请求还能顺手把 Bug 一并提了并且完全不挑版本新旧项目通吃。这篇文章就把完整的实现思路、步骤、踩坑记录一次讲透希望对也在做接口测试自动化的同行有帮助。适用人群手上有一堆接口要测、长期被禅道建用例和提Bug折磨的朋友想在自己团队内部做类似工具但还不知道怎么下手的测试开发以及单纯对 HAR 解析和禅道 API 对接感兴趣的人。1. 项目整体设计与核心思路拆解1.1 为什么选 HAR 作为数据来源HARHTTP Archive FormatHTTP 存档格式本质就是一个 JSON 文件浏览器开发者工具或抓包软件都可以导出。它完整记录了页面加载过程中每一个网络请求的细节请求 URL、请求方法、请求头、请求体、Cookie、响应状态码、响应头、响应体、耗时等。对测试工程师来说这几乎是现成的接口测试用例素材库。举个最直白的例子一个登录页面你在浏览器里手动操作一遍导出的 HAR 文件里就包含了登录接口的完整请求数据。以前要写一条登录用例得先去查接口文档再照着文档填参数现在 HAR 文件直接把你刚才操作的“真实战场”定格了参数全是真实的、环境是真实的、返回结果也是真实的。有人会问直接用自动化测试工具录制的数据不也一样吗不一样。HAR 是浏览器原生支持导出的格式完全不需要额外安装插件或工具也不依赖任何编程语言框架。测试组新来的同学哪怕是第一次接触这个工具也能在 30 秒内完成 HAR 导出上手成本极低。1.2 工具核心要解决的三件事设计这套“一键生成 Skill”时我给自己明确了三个必须解决的问题也是测试团队日常最痛的三个点。第一建用例要快。旧流程里一个接口从需求分析到禅道上建好用例至少得 15 到 30 分钟。这还只是单个接口。碰到一个版本有几十上百个接口光建用例的时间就够喝一壶了。新流程里HAR 文件导入后解析、格式化、自动生成用例标题和步骤整个过程控制在几秒内一次性能处理几十个请求。第二Bug 要能自动关联请求详情。以前提 Bug要么手动截图请求报文和响应报文要么粘贴一段日志信息不完整不说开发拿到 Bug 第一句话经常是“这个请求是在什么环境下发的参数是什么”。HAR 转 Bug 后请求的完整链路直接作为附件或描述内容进禅道开发打开就能复现沟通成本直线下降。第三不要被版本卡住。很多团队的自动化测试脚本和版本是强绑定的一旦项目的接口地址变了、参数结构调整了脚本就得跟着改。我们这套方案的本质是“读取 HAR 文件里的实际数据”而 HAR 文件始终来自浏览器实时导出所以不管项目迭代到什么版本只要浏览器能录到请求工具就能正确建用例和提 Bug——这正是“不受版本限制”的核心含义。1.3 整体流程和技术选型整个系统分成三层。第一层是数据输入层也就是 HAR 文件的获取。推荐用 Chrome 开发者工具的 Network 面板勾选 Preserve log保留日志在页面上完整操作一遍核心流程后右键选择 Save all as HAR with content另存为带内容的 HAR就能把完整的请求响应内容保存下来。第二层是核心处理层用 Python 编写解析脚本。选 Python 的原因很简单处理 JSON 有天然优势生态里又有 requests 库可以直连禅道 API代码量可以压缩到最低程度。整个解析脚本的核心就是读取 HAR 文件遍历 HTTP Archive 结构中的 entries 数组逐个提取请求和响应的关键字段。第三层是输出层对接禅道的 RESTful API。通过调用禅道开放的接口实现自动创建用例、创建 Bug、关联产品和模块。实际开发中我还会在本地维护一个简单的配置文件记录禅道的地址、账号、产品 ID 和默认模块这样脚本在不同项目间切换时只需要改配置文件不需要动代码。这套设计跑下来我的最大感受是工具解决的虽然只是“建用例、提Bug”这两个小动作但把测试人员从基础重复劳动里解放了出来省下的时间可以做更深入的需求分析和场景设计这才是真正有价值的产出。2. HAR 文件核心结构与解析要点2.1 HAR 文件结构快速扫盲HAR 文件的最外层是一个对象包含 log 字段log 内部是 version、creator、entries 三个主要部分。我们最关心的 entries 是一个数组数组里每一个元素代表一次完整的 HTTP 请求事务。每一个 entry 包含以下关键字段看懂这些字段是后续解析的基础startedDateTime请求开始时间ISO 8601 格式可以用来分析接口调用顺序和耗时分布。time整个请求的耗时单位是毫秒。request请求对象里面有 method、url、httpVersion、headers、queryString、cookies、headersSize、bodySize、postData 等。response响应对象里面有 status、statusText、httpVersion、headers、cookies、content、redirectURL、headersSize、bodySize 等。cache缓存信息自动测试时一般忽略。serverIPAddress服务器 IP有时候排查环境问题会用到。timings各个阶段的耗时拆解如发送时间、等待时间、接收时间等。对测试而言request.url 和 request.method 是生成用例标题和分类的关键request.postData 是准备测试数据的关键response.status 是判断用例是否通过的基准request.headers 里的 Cookie、Authorization 等字段是鉴权信息在生成用例时需特别注意脱敏处理。2.2 解析重点核心字段的提取规则在实际解析过程中有一个非常容易踩坑的地方不是所有 HAR 里的请求都适合生成测试用例。比如静态资源文件JS、CSS、图片、字体的请求对接口测试来说基本没有复用价值如果一股脑全解析进去生成的用例列表会被垃圾数据淹没。所以要设计一套过滤规则。我常用的规则是过滤掉 URL 中包含 .js、.css、.png、.jpg、.gif、.svg、.woff、.woff2、.ico 等静态资源后缀的请求。过滤掉请求类型为 image 或其他非 XHR/Fetch 资源类型的请求。过滤掉开发者工具自身发起的请求这些请求一般 URL 里带有 devtools 关键词或请求头里有特殊标记。手动配置一个排除列表把不需要的接口前缀加进去比如某些埋点接口、心跳接口。过滤之后就是对 request 对象里的 headers、postData 进行提取。headers 建议完整保留清空敏感字段值比如 Cookie 和 Authorization 用占位符替代对 queryString 参数做美化换行处理这样生成的用例步骤看起来才像人写的。2.3 解析中的编码和格式坑HAR 文件里的中文内容是以 Unicode 转义形式存在的直接读取会看到一串\uXXXX编码非常影响可读性。所以解析时必须统一做一次解码将 Unicode 转义还原为中文。有次我偷懒没做这个处理生成的用例标题全是编码字符禅道用例列表简直没法看。编码处理是最基础却最容易忽视的一环。另外 request.postData 里如果 Content-Type 是 application/jsonpostData.text 字段存储的是纯 JSON 字符串如果 Content-Type 是 application/x-www-form-urlencoded参数会出现在 postData.params 数组里每个参数包含 name 和 value 两个字段。解析时需要分别处理统一格式化为可读性较强的“参数名参数值”列表。注意HAR 文件默认不保留请求体和响应体的二进制数据如文件上传内容但只要在浏览器导出时勾选了保存内容文本类数据基本都能完整拿到。涉及文件上传的接口建议直接标记为“需手动补全文件”不做自动数据填入。3. 禅道对接与自动建用例、提 Bug 的实现3.1 禅道 API 对接前的基础准备禅道是很多团队都在用的项目管理和 Bug 跟踪工具其新版本提供了比较完善的 RESTful API 接口测试人员只需要通过 HTTP 请求即可完成登录和业务操作。在写脚本之前需要先去禅道后台确认以下几项信息这些信息在后续配置文件里都要用到禅道部署地址形如http://your.zentao.server/api.php/v1。具有创建用例和 Bug 权限的账号密码。产品 ID在禅道产品列表页面的 URL 里可以看到比如http://your.zentao.server/product-view-2.html里的数字 2 就是产品 ID。模块 ID可选不填则默认挂到产品根模块下。版本名称从配置文件读取写入用例或 Bug 时作为版本字段这样版本无论怎么变用例记录都带着当时生成它的版本信息。在禅道 API 使用前需要先调登录接口换取 Token。Token 是后续操作的身份凭证建议全局复用避免频繁登录触发账号锁定。我在脚本里做了一个 Token 管理函数启动时自动登录Token 过期后自动重新登录运行过程中完全不需要人工介入。3.2 自动建用例的逻辑设计建用例是这套 Skill 的第一步。从 HAR 解析出的每一条有效请求对应生成一条禅道测试用例用例的基本结构长这样用例标题【接口自动化】{请求方法} {接口路径}例如【接口自动化】POST /api/v1/user/login。优先级根据响应耗时或状态码自动赋值。耗时超过 3 秒设为 2响应状态非 2xx 或 3xx 设为 1其余默认设为 3。用例步骤第 1 步写明接口地址、请求方法、请求头、请求体第 2 步写明预期响应状态码范围如有重定向或鉴权字段单独补充一条步骤说明。预期结果默认断言响应状态码为 2xx/3xx耗时低于 3 秒响应体不为空。有更高要求的场景可以在生成后手动补充。关联需求如果 HAR 文件是通过配置指定了需求 ID则自动关联未指定则留空由测试人员后续在禅道界面手动关联。这里尤为关键的一点用例生成之后不能自动提交执行结果只负责建用例内容执行与否由测试人员决策。自动化工具的价值是辅助不是替代判断。我在第一版工具里曾经把所有请求的用例自动执行并把结果贴回去结果批量误报搞得团队怨声载道后来改成只建用例不自动执行口碑才慢慢好起来。3.3 自动提 Bug 的触发条件提 Bug 的逻辑相比建用例简单一些核心是对响应状态码和请求耗时做判断。我的设计是这样的状态码为 400、401、403、404、500、502、503 时判定为 Bug。状态码为 2xx 或 3xx但响应耗时超过 5 秒也判定为 Bug性能类。请求抛出的网络异常如超时、连接被拒绝判定为 Bug。过滤规则命中的例外列表如已知的错误注入接口不触发自动提 Bug。每条 Bug 的内容包括标题格式为【版本号】【接口自动化】接口名 状态码异常、描述附上请求 URL、请求方法、请求头、请求体、响应状态码、响应体截断内容、HAR 来源文件名、优先级、严重程度、指派给谁默认指派给配置文件中预先设置的产品负责人。提示自动提 Bug 前必须设计“二次确认”模式。我在脚本里加了个可配置项dry_runTrue时只打印将要创建的 Bug 信息不实际调用禅道 API。等测试人员人工确认无误后再切换为 False 批量提交。宁可多一步确认也不要让工具凌晨批量给开发派出一堆误报 Bug。3.4 版本无关性的实现要点版本无关是这套方案区别于传统脚本的核心优势。怎么做到的呢关键在于用例和 Bug 的“内容”全部来自浏览器实时导出的 HAR 文件而 HAR 文件的产生与项目版本无关——只要你当前的浏览器环境访问的是最新版本的被测系统导出的一定是最新格式的请求数据。脚本里唯一需要动态变化的是禅道里的产品版本号这个我从配置文件读取每测一个新版本只需在配置文件里改一个版本号字符串其他统统不用动。举个例子产品迭代到 3.2 版本测试人员先用浏览器走一遍 3.2 的登录、下单、支付流程导出 HAR脚本配置版本号改成v3.2跑一遍就自动生成 20 条用例和 2 个 Bug。3.3 发布了同样操作脚本代码零改动只需要换一个 HAR 文件、改一行版本号。这在传统脚本里根本不敢想传统脚本遇到接口参数调整还得改断言代码。4. 实操过程与核心环节实现4.1 环境准备与工具安装实操前先准备好基础环境。用到的软件和库如下Python 3.8建议用 3.10 或更高版本。requests 库pip install requests。json 库Python 自带无需安装。re 库Python 自带用于一些字段的加工处理。目录结构建议这样规划har2zentao/ ├── config.json # 配置文件存放禅道地址、账号、产品ID、版本号、过滤规则 ├── har_parser.py # HAR 文件解析模块 ├── zentao_client.py # 禅道 API 封装模块 ├── main.py # 主入口串联整个流程 ├── output/ # 存放解析结果和日志 └── har_files/ # 存放待处理的 HAR 文件这样的结构做出来后期接 CI 或者定时任务也很方便只需要在流水线里调用python main.py --file har_files/demo.har一个命令就够。4.2 主流程代码实现先说 HAR 解析模块。核心函数就是遍历 entries按过滤规则挑出有效请求再提取关键信息到一个统一的 dict 列表里。核心代码框架如下已简化突出思路import json def parse_har(file_path, exclude_exts(.js, .css, .png, .jpg)): with open(file_path, r, encodingutf-8) as f: har_data json.load(f) entries har_data[log][entries] results [] for entry in entries: request entry.get(request, {}) url request.get(url, ) method request.get(method, GET) # 静态资源过滤 path_lower url.lower() if any(path_lower.endswith(ext) for ext in exclude_exts): continue response entry.get(response, {}) status response.get(status, 0) item { url: url, method: method, status: status, time: entry.get(time, 0), request_headers: format_headers(request.get(headers, [])), request_query: format_query(request.get(queryString, [])), post_data: extract_post_data(request.get(postData, {})), response_body: extract_response_body(response.get(content, {})), started_datetime: entry.get(startedDateTime, ) } results.append(item) return results这里format_headers、format_query、extract_post_data等函数主要做字段格式化和敏感信息脱敏代码写得比较长不单独展开核心就是按照前文提到的规则处理。禅道 API 封装模块长这样import requests class ZentaoClient: def __init__(self, base_url, username, password): self.base_url base_url.rstrip(/) self.session requests.Session() self.token self.login(username, password) def login(self, username, password): resp self.session.post(f{self.base_url}/tokens, json{ account: username, password: password }) resp.raise_for_status() return resp.json()[token] def create_case(self, product_id, case_data): headers {Token: self.token, Content-Type: application/json} resp self.session.post( f{self.base_url}/products/{product_id}/cases, jsoncase_data, headersheaders ) resp.raise_for_status() return resp.json() def create_bug(self, product_id, bug_data): headers {Token: self.token, Content-Type: application/json} resp self.session.post( f{self.base_url}/products/{product_id}/bugs, jsonbug_data, headersheaders ) resp.raise_for_status() return resp.json()API 的基本调用逻辑就两个函数一个建用例、一个建 Bug剩下的全在主流程里做判断和组装。主流程 main.py 大概是这个节奏import argparse, json, logging def main(): parser argparse.ArgumentParser() parser.add_argument(--file, requiredTrue, helpHAR file path) parser.add_argument(--dry-run, actionstore_true, helpdry run mode) args parser.parse_args() config json.load(open(config.json, encodingutf-8)) parsed parse_har(args.file) client ZentaoClient(config[base_url], config[username], config[password]) for item in parsed: case_data build_case_data(item, config) if args.dry_run: logging.info([DRY RUN] create case: %s, case_data[title]) continue client.create_case(config[product_id], case_data) # Bug 判断逻辑单独一个函数 bug_candidates filter_bug_candidates(parsed, config) for bug_item in bug_candidates: bug_data build_bug_data(bug_item, config) if args.dry_run: logging.info([DRY RUN] create bug: %s, bug_data[title]) continue client.create_bug(config[product_id], bug_data) logging.info(All done. Total cases: %d, bugs: %d, len(parsed), len(bug_candidates)) if __name__ __main__: main()实际运行效果是短则一两秒长则十几秒取决于 HAR 文件里有效请求的数量和禅道服务器的响应速度。我自己测试过包含 87 个有效接口的 HAR 文件从跑脚本到禅道刷出来用例大概只花了一分钟左右这在以前需要整个下午。4.3 配置文件的细节设计config.json 的配置项值得单独说明一下这是脚本能在不同项目间复用而不改代码的关键{ base_url: http://your.zentao.server/api.php/v1, username: test_automation, password: your_password, product_id: 12, version: v3.2, default_assigned_to: dev_lead, default_module_id: 0, filter_url_keywords: [/monitor, /health, /sockjs], exclude_status_codes: [401, 403], dry_run: true, max_response_body_preview: 2000 }每个字段的用途base_url / username / password禅道连接信息可以从环境变量读取不建议硬编码在源码里。product_id当前要建用例和 Bug 的产品 ID。version当前被测版本号会拼接到用例标题和 Bug 标题里。default_assigned_toBug 默认指派给谁填禅道账号。default_module_id模块 ID填 0 表示根模块。filter_url_keywordsURL 里包含这些关键词的请求直接跳过用于排除埋点、健康检查等无价值接口。exclude_status_codes这些状态码即使异常也不自动提 Bug比如某些项目里 401 是正常的未登录业务分支。dry_run默认开启确认无误再手动改为 false。每次到新项目上只需要改这些配置项Python 代码一行不动。这套设计思路也直接呼应了“不受版本限制”——版本变了只改一个字符串。4.4 用例步骤生成的排版细节生成的用例步骤如果在禅道上是一堆乱糟糟的原始 JSON测试人员看了会崩溃不说后续检索和手工补充都困难。所以我在生成步骤时非常注重排版。一个标准用例的 steps 数组长这样[ {step: 接口地址POST https://api.example.com/v1/user/login, expect: 状态码非4xx/5xx}, {step: 请求头Content-Type: application/json; Authorization: ******, expect: 鉴权通过}, {step: 请求体{\username\: \${USERNAME}\, \password\: \${PASSWORD}\}, expect: 返回包含 token 字段}, {step: 接口耗时256 ms, expect: 不超过3000ms} ]注意请求体里的用户名密码我在解析时做了关键词替换把抓到的真实值替换成占位符避免账号密码泄露。具体做法是对 postData 的 value 值做正则匹配凡是 key 名包含 pass、pwd、token、secret 的一律替换成${MASKED}。这个处理非常必要尤其当 HAR 文件准备归档或传给其他人时。5. 常见问题与排查技巧实录5.1 Token 失效与禅道接口返回 401这是最开始接入禅道 API 时遇到最频繁的问题。禅道的 Token 有效期默认较短脚本长时间跑批时Token 中途过期后续请求直接 401整个流程中断。我的解决方案是在 ZentaoClient 里实现自动重试机制每个 API 调用前先检查当前 Token 是否存在收到 401 响应时调用登录接口刷新 Token然后用新 Token 重试一次原请求。这样整个批量过程完全无感。5.2 HAR 文件过大导致解析慢录制时间较长的大 HAR 文件可能有几百甚至上千条请求解析速度变慢内存占用飙升。碰到过一份 300MB 的 HAR 文件脚本跑了一分多钟才出结果。优化策略有两个方向一是在解析前先做一个文件级压缩只提取需要的字段丢弃 response.content 里的大体积数据二是用流式处理避免一次把整个文件读进内存。实际测试下来处理 300MB 文件的时间从 60 秒降到了 8 秒左右。注意HAR 文件里常见的体积大头是 Base64 编码的小图片或字体文件这类内容根本不需要生成用例可以在解析阶段直接跳过。解析前先执行一次静态资源过滤效率能提升一半以上。5.3 Cookie 与鉴权信息的跨环境失效问题有些接口在浏览器里访问时带着登录态的 Cookie直接从 HAR 提取 Cookie 塞到用例里在测试环境下可能是失效的。这个问题不死磕的话生成的用例执行时会大面积失败但不是接口真的坏了而是鉴权过期。我建议的方案是解析 HAR 时只提取请求头里与鉴权无关的字段Cookie 和 Authorization 统一用占位符生成用例时额外增加一条前置条件说明“需先在禅道用例执行前更新有效的 Cookie 或 Token”。如果团队内部有统一的鉴权服务可以在提取时调用鉴权服务生成临时 Token 做替换。5.4 重复接口请求的去重处理用户在一次页面操作中同一个接口可能被重复请求多次比如翻页、重复点击直接全部解析会生成重复用例。我的去重逻辑是以“请求方法 URL 请求体前 200 字符”作为一个组合键如果键值相同只保留第一次出现的记录并在用例步骤末尾追加“实际调用次数N”。这个细节能让用例列表干净很多同时保留了一个额外信息接口被频繁调用对性能测试场景有参考意义。5.5 常见问题速查表现象可能原因解决办法解析出的 URL 里有大量转义符号HAR 文件未做 Unicode 解码在解析前统一对 JSON 内容做一次 ensure_asciiFalse 解码处理禅道创建用例接口返回 400必填字段缺失或格式错误检查 steps 数组结构确认 title 非空确认 product_id 存在于禅道自动生成的用例没有挂到正确模块模块 ID 配置错误或模块不存在到禅道界面找到正确模块 ID 填入配置Bug 标题里版本号始终是旧版本忘记改 config.json 的 version 字段每次跑批前检查配置文件或者用命令行参数覆盖版本号HAR 导出后请求为空浏览器 Network 面板未勾选 Preserve log打开设置勾选后才能保留跳转前后的全部请求6. 效率提升实测与后续扩展思考6.1 一次真实项目的效率数据拿一个实际电商后台项目举例被测范围是商品管理模块的 23 个核心接口。旧流程里人工建用例、写步骤、贴请求数据再人为构造几个异常请求去提 Bug测完整个模块大约用了两天半。用这套 HAR 一键生成 Skill操作如下用浏览器完整操作一遍商品的新增、编辑、上架、下架、删除流程导出 HAR配置版本号跑脚本总计 30 分钟生成了 21 条用例和 3 个 Bug其中 2 个是响应耗时超过阈值1 个是状态码异常。这只是单模块。整个版本跑下来原本需要近一周的“用例建设 基础 Bug 提交”第一次全量用这套工具只花了一天不到。省下来的时间全部投入到了更深入的场景测试、异常流测试和性能分析上。6.2 这个工具还能怎么扩展第一个扩展方向是接入 CI 流水线。HAR 文件可以自动化从浏览器录制工具或云端测试环境中定时导出配合定时任务每天跑一次相当于每天对核心链路做一次自动化巡检生成的用例和 Bug 自动进禅道。版本更新后再也不用担心测试用例没有跟上代码变更因为每次跑批的用例都是基于最新 HAR 的真实数据。第二个扩展方向是参数化与数据驱动。目前提取请求体还是“把真实值替换为占位符”后续可以引入一套映射表把 key 和测试数据池关联起来运行时自动填充不同的数据组合这样用例就能从“验证某一个真实场景”扩展为“验证多组数据场景”测试覆盖面更广。第三个方向是报告生成。可以在输出层增加一个 HTML 报告把每个请求的解析结果、用例生成情况、Bug 触发情况汇总成一个对团队可见的报表方便在晨会上快速同步测试进展。6.3 上线前的一些提醒最后说几个团队在落地这套工具时容易忽略的问题。权限管理必须上心。脚本用到的禅道账号建议单独建一个专用账号权限控制在用例和 Bug 的创建、编辑、查看范围不要给管理员权限。这样即使脚本代码或 Token 泄露影响面也能控制住。HAR 文件的隐私问题要重视。浏览器导出的 HAR 可能包含用户的个人访问信息、公司内网的地址、甚至部分加密前的敏感字段。所以团队如果要用这套工具先定好 HAR 文件的流转和清理制度用完即删如果要归档务必先脱敏。配置文件的敏感信息不能入库。禅道密码、Token 等建议通过环境变量或专门的密钥管理服务注入不要直接写在 config.json 里提交到代码仓库。团队里发生过一次密码硬编码泄露的事教训真的深刻。最后分享一点个人体会工具本身并不复杂复杂的是如何让工具贴合团队实际的工作节奏。我在设计这套 Skill 时最深的一个感受是很多自动化方案追求全自动、零人工但在实际工程环境里留一点人工确认的口子反而让工具更容易被团队接受。自动生成用例和 Bug 草稿、人工审核确认后再提交既保证了效率也把误判和风险控制在了可接受的范围内。这也是我把 dry_run 设计成默认开启并且把过滤规则全部配置化的核心原因。如果你所在的团队也每天被建用例和提 Bug 这件事绑住了手脚不妨按这个思路自己搭一套试试。成本不高工具代码也就两三百行但省下来的时间足以让你去做自己真正应该做的测试设计。踩过了坑再回头看我上面写的这些问题你会觉得都是小 case。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑