接口测试全流程实战:从Postman功能测试到JMeter性能压测

发布时间:2026/6/26 16:04:18
接口测试全流程实战:从Postman功能测试到JMeter性能压测 1. 项目概述为什么接口测试是研发效能的核心最近在带团队做项目复盘发现一个老生常谈的问题又冒出来了线上出故障了一查原因是后端接口改了字段前端没同步更新导致页面数据渲染异常。开发说“我自测过了”测试说“我测了页面功能”但就是没人去专门验证一下那个数据接口返回的JSON结构对不对。这种场景但凡在一线干过两年以上的估计都遇到过不止一次。说到底还是对接口测试的重视程度和实战方法没到位。今天我就结合自己踩过的坑和趟出来的路用一个完整的实战案例把接口测试这件事掰开揉碎了讲清楚。这不是一篇教你点哪个按钮的教程而是一个从认知、设计、工具选型到落地执行的全流程实战指南。我会用一个模拟的“用户中心”微服务项目作为背景覆盖从单接口功能验证、多接口业务流测试到性能、安全、持续集成的完整链条。目标是让你看完之后不仅能动手写出有效的接口用例更能建立起一套可持续运行的接口测试体系真正把它变成研发流程里坚不可摧的一环。接口测试为什么这么重要因为它卡在了一个非常关键的位置上——它是前后端、服务与服务之间的契约。前端页面再花哨业务逻辑再复杂最终都要通过一个个API来交换数据。这个契约要是出了问题就像房子的承重墙裂了缝外表再光鲜也白搭。而且接口测试的投入产出比极高它执行快、自动化程度高、发现问题早是提升交付质量和效率的利器。2. 实战环境搭建与核心思路解析2.1 案例背景一个典型的用户中心微服务为了不让内容飘在空中我们虚构一个足够典型但又不过于复杂的项目“用户中心”微服务。它提供以下核心接口这也是绝大多数系统都会有的模块用户注册(POST /api/v1/users/register): 接收用户名、密码、邮箱等信息创建新用户。用户登录(POST /api/v1/users/login): 验证用户名密码返回访问令牌Token。查询用户信息(GET /api/v1/users/{userId}): 根据用户ID获取用户详情需要Token认证。更新用户信息(PUT /api/v1/users/{userId}): 修改用户昵称、头像等信息需要Token认证。删除用户逻辑删除(DELETE /api/v1/users/{userId}): 将用户状态标记为失效需要管理员权限。这个服务使用 RESTful 风格设计返回标准 JSON 格式并且引入了 Token 鉴权。我们将围绕它展开所有测试。2.2 工具选型Postman、JMeter 还是 Apifox工欲善其事必先利其器。市面上接口测试工具很多新手容易挑花眼。我的选型逻辑很简单根据测试阶段和目的组合使用而不是死守一个。Postman功能测试与协作的首选Postman 的优势在于其极佳的用户体验和对 API 开发全生命周期的支持。它特别适合用于接口调试与探索性测试图形化界面操作方便快速验证接口是否通。功能测试用例管理与自动化它的 Collection集合和 Runner运行器功能强大可以很好地组织和管理我们的功能测试用例。团队协作与文档可以方便地分享集合生成接口文档。Mock 服务在前后端并行开发时可以用它快速 Mock 数据不阻塞进度。 所以在我们的实战中单接口功能测试、业务流程串联测试、以及日常的接口调试主要使用 Postman。JMeter性能压测的王者JMeter 是纯血统的性能测试工具虽然它也能做功能测试但界面和操作对于纯功能验证来说不够友好。它的核心价值在于模拟高并发轻松创建数百、数千个虚拟用户。丰富的监听器提供吞吐量、响应时间、错误率等详尽的性能报表。分布式压测支持多机负载产生更大的压力。 因此当我们需要验证接口的性能瓶颈、容量规划时必须请出 JMeter。ApifoxAll-in-One 的新选择Apifox 是后起之秀它试图整合 Postman调试、Swagger文档、Mock.jsMock、JMeter性能的功能。它的优势是“一体化”避免在多个工具间切换。对于中小团队或新启动的项目用 Apifox 统一管理可能效率更高。但它在某些深度功能上如 JMeter 的极致压测能力、Postman 的复杂脚本支持可能不如单一工具强大。如果你的团队还没有形成固定的工具链追求开箱即用和统一管理Apifox 是一个不错的起点。我的实战策略是用Postman作为功能测试和自动化核心用JMeter专攻性能测试。两者通过导出/导入如 CSV 数据文件等方式进行数据互通。这样既能发挥各自长处流程上也清晰。2.3 测试金字塔思维在接口层的应用很多人做接口测试就是对着文档一个个参数去试这是远远不够的。我们需要有层次地设计测试。单接口功能测试基础层验证每个接口本身是否按预期工作。这是基石要覆盖正向用例参数正确预期成功。反向用例错误注入参数缺失、类型错误、长度超限、重复数据、边界值等。鉴权验证Token 缺失、错误、过期权限不足如普通用户尝试删除他人。多接口业务流程测试集成层模拟用户实际操作路径。例如“注册 - 登录 - 查询信息 - 更新信息”就是一个业务流程。这能发现接口间数据依赖和状态传递的问题。数据驱动测试将测试数据如用户名、密码从测试脚本中分离出来用外部文件如 CSV管理。这样一套脚本可以运行多种数据组合极大提高覆盖率和维护性。契约测试可选但高级使用像 Pact 这样的工具保障服务提供者和消费者之间的接口契约不被意外破坏在微服务架构下尤其有用。我们的实战将重点覆盖前三个层次。3. 单接口功能测试实战以用户登录为例让我们从最核心的登录接口 (POST /api/v1/users/login) 开始深入每一个测试细节。3.1 接口文档解析与测试点提取假设我们拿到这样一份简化的接口文档URL:http://api.example.com/api/v1/users/loginMethod: POSTHeaders:Content-Type: application/jsonRequest Body (JSON):{ username: string, 必填长度3-20, password: string, 必填长度6-20密文传输 }Response (Success, 200):{ code: 200, message: success, data: { userId: 123, username: testuser, token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..., expiresIn: 7200 } }Response (Error, e.g., 401):{ code: 401, message: 用户名或密码错误, data: null }基于这份文档我们可以拆解出以下测试点测试类别测试场景请求参数预期结果正向功能使用正确的用户名密码登录{“username”: “validUser”, “password”: “validPass123”}HTTP 200返回包含有效 token 的用户信息反向功能用户名错误{“username”: “wrongUser”, “password”: “validPass123”}HTTP 401错误信息提示密码错误{“username”: “validUser”, “password”: “wrongPass”}HTTP 401错误信息提示用户名为空{“username”: “”, “password”: “validPass123”}HTTP 400提示参数校验失败密码为空{“username”: “validUser”, “password”: “”}HTTP 400提示参数校验失败用户名长度不足{“username”: “ab”, “password”: “validPass123”}HTTP 400提示长度不符合要求密码长度不足{“username”: “validUser”, “password”: “123”}HTTP 400提示长度不符合要求请求体格式错误非JSONusernamevalidUserpassword123(Form-Data)HTTP 415 或 400提示不支持媒体类型缺少必要字段{“username”: “validUser”}HTTP 400提示缺少参数边界与异常SQL注入尝试{“username”: “admin --”, “password”: “any”}HTTP 401 或 400绝不能返回系统错误或执行成功超长字符串攻击{“username”: “A”*1000, “password”: “A”*1000}HTTP 400 或 413服务应能优雅处理不崩溃3.2 在 Postman 中构造与管理测试用例创建集合与环境变量在 Postman 中新建一个集合命名为用户中心接口测试。创建环境变量如base_url(http://api.example.com)。这样切换测试环境开发、测试、预生产时只需改环境变量不用改每一个请求的URL。编写第一个正向用例在集合下新建请求命名为[正向]用户登录-成功。Method 选POSTURL 填{{base_url}}/api/v1/users/login。Body 选raw-JSON填入正确的用户名密码。点击Send发送查看返回结果是否符合预期。为请求添加自动化断言 只靠人眼检查响应是不够的必须用脚本断言。在请求的Tests标签页里我们可以用 JavaScript 编写断言。// 1. 验证状态码为200 pm.test(“Status code is 200”, function () { pm.response.to.have.status(200); }); // 2. 验证响应体包含成功的code pm.test(“Response has success code”, function () { var jsonData pm.response.json(); pm.expect(jsonData.code).to.eql(200); }); // 3. 验证响应中包含token字段 pm.test(“Response contains token”, function () { var jsonData pm.response.json(); pm.expect(jsonData.data).to.have.property(‘token’); pm.expect(jsonData.data.token).to.be.a(‘string’).that.is.not.empty; }); // 4. 验证响应结构是否符合文档约定更严格的断言 pm.test(“Response schema is valid”, function () { var schema { “type”: “object”, “properties”: { “code”: {“type”: “number”}, “message”: {“type”: “string”}, “data”: { “type”: “object”, “properties”: { “userId”: {“type”: “number”}, “username”: {“type”: “string”}, “token”: {“type”: “string”}, “expiresIn”: {“type”: “number”} }, “required”: [“userId”, “username”, “token”, “expiresIn”] } }, “required”: [“code”, “message”, “data”] }; pm.response.to.have.jsonSchema(schema); });每次请求发送后Postman 会自动运行这些测试脚本并在Test Results面板显示通过或失败。这是接口自动化的核心一步。参数化与数据驱动测试 我们不可能为每一组错误数据都新建一个请求。这时可以用Collection Runner或Newman(Postman的命令行工具) 配合数据文件。创建一个 CSV 文件login_data.csvtest_case,username,password,expected_status,expected_code,expected_message 正向用例,testuser,password123,200,200,success 用户名为空,,password123,400,400,用户名不能为空 密码为空,testuser,,400,400,密码不能为空 用户名错误,wronguser,password123,401,401,用户名或密码错误 密码错误,testuser,wrongpass,401,401,用户名或密码错误在 Postman 请求中将 Body 里的固定值替换为变量{{username}}和{{password}}。在Tests脚本中使用pm.iterationData.get(“expected_status”)等方式来获取 CSV 中当前行的预期值进行动态断言。在 Collection Runner 中导入这个 CSV 文件并运行Postman 会逐行读取数据替换变量发送请求并断言。这样一来一个请求就能覆盖无数测试数据组合。实操心得断言要“狠”也要“聪明”“狠”断言不要只检查状态码。业务状态码code、关键业务字段如token、userId、响应时间pm.response.responseTime应小于某个阈值都要断言。对于重要接口甚至可以对返回的每个字段做类型和值域的校验。“聪明”利用 Postman 的pm.response.json()获取响应数据并将其存储为环境变量或全局变量供后续请求使用。比如把登录返回的token存到环境变量access_token里这样后面所有需要鉴权的请求都可以在 Headers 中直接使用{{access_token}}实现接口间的状态传递。3.3 处理鉴权Token 的动态获取与传递在我们的案例中查询和更新用户信息都需要 Token。最佳实践是自动化获取 Token。在登录请求的Tests脚本中提取并保存 Tokenif (pm.response.code 200) { var jsonData pm.response.json(); // 将 token 保存到环境变量中 pm.environment.set(“access_token”, jsonData.data.token); // 也可以保存 userId供后续请求使用 pm.environment.set(“current_user_id”, jsonData.data.userId.toString()); }在需要鉴权的请求中使用变量设置 Header在“查询用户信息”请求的 Headers 中添加Authorization: Bearer {{access_token}}将 URL 中的{userId}替换为{{current_user_id}}。这样只要先运行登录请求并成功后续请求就能自动带上有效的 Token。使用 Postman 的“预请求脚本”自动处理 Token 过期 更高级的做法是在每一个需要鉴权的请求的Pre-request Script标签页里写一段脚本检查当前access_token是否过期如果服务端返回了过期时间expiresIn可以计算如果即将过期则自动调用登录接口刷新 Token。这能保证长时间的自动化测试任务不会因 Token 失效而中断。4. 多接口业务流程测试实战单接口测试通过不代表业务能跑通。我们需要模拟真实用户的连续操作。4.1 构建“用户全生命周期”测试流我们设计一个核心业务流程注册新用户 - 使用新用户登录 - 查询自身信息 - 更新昵称 - 再次查询验证更新。在 Postman 中这可以通过Collection Runner或更强大的Flows新功能来实现。方法一使用 Collection Runner 和脚本控制流程在集合中按顺序排列请求[注册] - [登录] - [查询] - [更新] - [查询]。关键点在于数据传递注册请求的Tests脚本中需要从响应里提取自动生成的userId或username并保存到变量如new_username。if (pm.response.code 200) { var jsonData pm.response.json(); pm.environment.set(“new_username”, jsonData.data.username); // 注意注册后通常不会直接返回密码这里我们假设使用注册时设定的固定密码 // 更佳实践是注册和登录使用同一套动态生成的数据 }登录请求的 Body 中用户名使用{{new_username}}密码使用注册时设定的固定密码或另一个变量。登录成功后按之前的方法保存token和userId。后续的查询、更新请求都使用这些动态变量。在 Collection Runner 中运行整个集合Postman 会按顺序执行并自动处理变量传递。方法二使用 Postman Flows可视化编排对于更复杂的、有分支判断的业务流例如登录失败则跳转到注册Flows 提供了图形化界面可以通过拖拽和连接节点来设计测试流程逻辑更清晰直观。踩坑记录接口间的数据污染与清理业务流程测试最大的坑是“数据污染”。你注册的用户名如果已存在用例就会失败。因此有几种策略每次使用唯一数据在预请求脚本中用Date.now()或Math.random()生成唯一用户名如testuser_1689923456789。这是最推荐的方式。测试前置清理在业务流程开始前调用一个“清理接口”如果开发提供或直接操作测试数据库删除测试产生的数据。这需要更高的权限和更复杂的准备。测试后置清理流程跑完后调用删除接口清理数据。但要确保删除接口本身是可靠的否则可能留下垃圾数据。务必记住自动化测试不应该依赖固定的测试数据而应该能够自己创建和清理数据。4.2 使用 Newman 进行命令行持续集成Postman 的图形化界面适合调试和管理但真正的自动化集成需要命令行工具Newman。导出集合与环境从 Postman 将你的测试集合和环境变量导出为 JSON 文件如user_center_collection.json和test_env.json。安装与运行 Newman# 全局安装 Newman npm install -g newman # 基本运行命令 newman run user_center_collection.json -e test_env.json # 生成 HTML 测试报告更直观 newman run user_center_collection.json -e test_env.json -r html,cli --reporter-html-export report.html集成到 CI/CD 流水线在 Jenkins、GitLab CI、GitHub Actions 等工具中将上述 Newman 命令作为一个步骤加入。每次代码提交或构建完成后自动运行接口测试套件并根据测试结果通过/失败决定是否继续后续部署流程。# 示例GitHub Actions 片段 - name: Run API Tests with Newman run: | npm install -g newman newman run postman/collection.json -e postman/environment.json --reporters cli,html --reporter-html-export newman-report.html continue-on-error: false # 如果测试失败则终止工作流数据驱动与参数化Newman 同样支持通过--iteration-data参数指定外部的 CSV 或 JSON 数据文件实现数据驱动测试。将 Postman Newman 接入 CI/CD意味着你的接口测试从“可执行”变成了“必须执行”的质量关卡这是质的变化。5. 接口性能测试实战使用 JMeter功能没问题了那能扛住多少压力呢这就需要 JMeter 出场了。5.1 从 Postman 到 JMeter测试脚本的转换虽然可以手写 JMeter 脚本但更高效的方式是利用 Postman 已有的请求。导出 Postman 集合在 Postman 中将你的集合导出为Collection v2.1格式的 JSON 文件。使用 JMeter 的 “HTTP(S) Test Script Recorder” 或第三方工具JMeter 本身没有直接导入 Postman 集合的功能。一个取巧的办法是在 JMeter 中设置好代理录制器Test Script Recorder。在 Postman 中将代理设置为 JMeter 代理默认localhost:8888。在 Postman 中手动运行一遍你的集合请求。JMeter 会录制下所有这些 HTTP 请求并生成对应的HTTP Request采样器。然后你在这个基础上进行组织和参数化。使用开源转换工具社区有一些工具如postman-to-jmeter可以进行转换但可能需要对生成的 JMX 文件进行一些调整。对于我们的登录接口压测我们也可以直接手写一个简单的 JMeter 脚本这有助于理解 JMeter 的核心组件。5.2 构建一个基础的登录压测脚本创建测试计划打开 JMeter新建一个Test Plan。添加线程组右键 Test Plan - Add - Threads (Users) - Thread Group。这里设置并发数。Number of Threads (users): 100 模拟100个并发用户Ramp-up period (seconds): 10 在10秒内启动所有100个用户Loop Count: Forever 一直循环直到手动停止或达到时长限制添加 HTTP 请求默认值右键 Thread Group - Add - Config Element - HTTP Request Defaults。这里设置所有请求共享的服务器和端口如http://api.example.com避免每个请求重复填写。添加 HTTP 请求采样器右键 Thread Group - Add - Sampler - HTTP Request。Name:用户登录接口Path:/api/v1/users/loginMethod:POSTBody Data: 填入 JSON 格式的请求体如{“username”: “${username}”, “password”: “${password}”}添加 CSV 数据文件设置为了模拟不同用户登录我们需要参数化。右键 Thread Group - Add - Config Element - CSV Data Set Config。Filename: 指向一个user_credentials.csv文件内容为username,password的多行数据。Variable Names:username,password其他选项默认。这样每个虚拟用户线程在执行时都会从 CSV 文件中读取一行数据作为变量。添加监听器查看结果右键 Thread Group - Add - Listener - View Results Tree / Summary Report / Aggregate Report。View Results Tree在调试时有用但正式压测时因其消耗资源巨大务必禁用。主要看Summary Report或Aggregate Report。5.3 关键性能指标分析与解读运行压测后我们需要关注 JMeter 报告中的这些核心指标样本数 (Samples): 总共发出的请求数量。平均响应时间 (Average): 所有请求的平均耗时单位毫秒。这是最直观的体验指标。中位数 (Median): 50% 的请求响应时间低于此值。它比平均值更能抵抗极端值的影响。90%/95%/99% 百分位 (90% Line, etc.): 例如 90% Line500ms表示 90% 的请求响应时间在 500ms 以内。这个指标对于评估服务稳定性至关重要它告诉我们绝大多数用户的体验。吞吐量 (Throughput): 单位时间每秒内服务器处理的请求数。这是系统处理能力的核心指标。错误率 (Error %): 失败请求的百分比。在性能测试中即使响应慢只要返回了业务结果哪怕是错误码也可能不算“错误”。JMeter 默认将 HTTP 状态码非 2xx/3xx 的视为错误。我们需要根据业务定义来调整有时需要通过Response Assertion来判断业务逻辑是否成功。接收/发送字节数 (KB/sec): 网络吞吐量。如何分析假设我们对登录接口压测 5 分钟得到如下近似数据平均响应时间120ms95% 响应时间350ms吞吐量200 req/sec错误率0%这看起来不错。但我们需要结合业务预期来判断如果这是一个后台管理系统的登录这个性能完全足够。如果这是一个千万级日活 App 的登录接口在促销时瞬时并发可能达到上万。那么 200 req/sec 的吞吐量可能就是瓶颈需要进一步做压力探测找到性能拐点何时错误率上升或响应时间急剧增加。性能测试核心心法不要一上来就怼高并发阶梯加压使用Concurrency Thread Group或Stepping Thread Group插件从低并发如10用户开始逐步增加每30秒增加50用户观察系统性能曲线的变化找到性能拐点。思考时间与 pacing真实用户操作之间有间隔。在 JMeter 中可以使用Constant Timer或Gaussian Random Timer来模拟用户思考时间使测试更贴近真实场景。监控系统资源光压测客户端不够必须同时监控服务器的 CPU、内存、磁盘 I/O、网络带宽以及数据库连接数、慢查询等。使用nmon、GrafanaPrometheus等工具。瓶颈可能不在应用代码而在数据库或某个中间件。断言业务成功在 JMeter 的 HTTP 请求下添加Response Assertion检查响应体中是否包含”code”:200。否则可能服务器返回了 HTTP 200但业务逻辑是失败的如“密码错误”这会被 JMeter 误判为成功请求。6. 接口安全测试要点接口测试不能只关心功能与性能安全是底线。以下是一些必须检查的常见安全漏洞点认证与鉴权绕过测试点尝试在未登录状态下直接访问需要 Token 的接口如查询用户信息。尝试使用已过期、伪造或属于其他用户的 Token 进行访问。工具辅助可以使用 Burp Suite 等工具拦截和重放请求修改 Token 或直接删除 Authorization Header。敏感信息泄露测试点检查接口响应中是否返回了不必要的敏感信息如数据库主键 ID、密码明文、内部错误堆栈信息、服务器版本等。案例登录失败时错误信息是“用户名不存在”还是“密码错误”前者会暴露系统中是否存在某个用户属于信息泄露。参数注入SQL 注入在输入参数中尝试‘ OR ‘1’’1、‘; DROP TABLE users; --等 payload。观察响应是否异常如报数据库错误或结果异常如返回了所有用户数据。命令注入如果接口涉及文件操作或系统调用尝试; ls -la、| cat /etc/passwd等 payload。工具辅助SQLMap 可以自动化检测 SQL 注入漏洞。越权访问水平越权用户A能否通过修改userId参数访问到用户B的数据这是最常见的越权漏洞。我们的测试用例中需要用两个不同的用户 Token 来交叉验证。垂直越权普通用户能否访问或操作需要管理员权限的接口如用户删除接口输入验证缺失测试点发送超长字符串、特殊字符、负数、零、浮点数给期望整数的参数发送数组给期望字符串的参数等。服务端应有严格的参数校验和类型转换并返回明确的错误信息而不是崩溃或产生异常行为。安全测试通常需要更专业的工具如 Burp Suite、OWASP ZAP和思维但作为接口测试的一部分以上这些基本检查应该成为测试用例库的标配。7. 常见问题排查与调试技巧即使计划再周密实战中也会遇到各种问题。这里记录几个高频问题的排查思路。问题一Postman 脚本里pm.environment.get取不到值可能原因1变量作用域错了。Postman 有全局变量、集合变量、环境变量、局部变量Data等多个作用域。确保你set和get的是同一个作用域。可能原因2变量名拼写错误。大小写敏感。可能原因3在 Collection Runner 中运行但没有为当前运行选择正确的环境。排查在脚本中用console.log(pm.environment.toObject())打印所有环境变量查看。问题二JMeter 压测时吞吐量上不去但服务器资源还很空闲可能原因1JMeter 客户端自身成为瓶颈。检查运行 JMeter 的机器 CPU、内存、网络是否已打满。可以尝试分布式压测。可能原因2线程组配置不当。Ramp-up时间太短线程启动开销大或者没有设置循环次数和持续时间。可能原因3测试脚本中存在不必要的监听器如 View Results Tree消耗了大量资源。可能原因4被压测的服务有连接数限制或频率限制限流。排查先在 JMeter 客户端机器上用top或任务管理器查看资源使用情况。简化测试脚本禁用所有监听器再试。查看服务器端日志和网络连接状态。问题三接口返回成功但业务状态码是错误JMeter 如何断言解决方案使用JSON Extractor或Regular Expression Extractor后置处理器先从响应体中提取出code字段的值保存到一个变量如biz_code。然后添加一个Response Assertion不是断言响应文本而是断言这个变量biz_code是否等于200。或者使用更灵活的JSR223 Assertion用 Groovy 脚本编写自定义断言逻辑。问题四依赖第三方接口不稳定导致自动化测试经常失败解决方案1治标在测试脚本中加入重试机制。Postman 可以通过在Tests脚本中递归调用setTimeout或使用postman.setNextRequest实现有限重试。JMeter 可以使用Retry Logic Controller。解决方案2治本对第三方接口进行Mock。在测试环境中使用 WireMock、MockServer 或者 Postman Mock Server 来模拟第三方接口的稳定返回。这是实现测试隔离和稳定的关键实践。问题五测试数据管理混乱经常因为数据冲突失败解决方案建立严格的测试数据治理策略。前缀或模式化所有自动化测试创建的数据都带有固定前缀如auto_test_user_。便于识别和清理。事前构造在测试套件开始前通过脚本或准备接口插入测试所需的基准数据。事后清理在测试套件结束后通过脚本或清理接口删除auto_test_前缀的所有数据。可以将清理脚本也集成到 CI/CD 流水线中。使用独立的测试数据库或 Schema这是最彻底的方式但成本也最高。接口测试不是一次性的任务而是一个需要持续维护和优化的过程。随着接口的迭代测试用例也需要同步更新。将其纳入 CI/CD让自动化测试成为守护质量的第一道防线才能真正释放开发者的精力让大家对每次发布都更有信心。从今天开始不妨就从你负责的那个核心接口开始用 Postman 写出第一个带有完整断言的测试用例吧。