ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

接口自动化测试实战:添加博客接口用例设计与数据驱动实现

接口自动化测试实战:添加博客接口用例设计与数据驱动实现 1. 用例编写前的接口分析先搞清楚添加博客到底测什么1.1 接口定义与业务规则梳理接上一篇我们框架已经能跑通登录用例了公共方法、配置管理、报告输出也都就位了。这一篇要做的添加博客是博客系统里最核心、也是最能体现测试设计功力的接口。为什么这么说因为登录接口的参数就那三四个而添加博客接口往往涉及标题、正文、分类、标签、封面图、发布状态等一长串字段还牵扯到用户权限、数据唯一性、状态流转这些业务逻辑随便一列就是二三十条用例。先别急着写代码。我写接口用例有个习惯先对着接口文档把关键信息捋一遍不然漏了约束条件后面用例设计全是空中楼台。拿一个通用博客系统为例接口定义大致是这样POST /api/blog/add Content-Type: application/json Authorization: Bearer {token}请求体{ title: 接口自动化测试实战, content: 文章正文内容, categoryId: 12, tags: [自动化测试, 接口测试], status: 1, coverImage: https://xxx.com/cover.png }但接口文档通常不会把业务规则写全这就需要你去翻产品文档、问开发甚至自己动手验证。我梳理添加博客的规则时重点关注下面几类必填项title、content 一般是必填的categoryId 通常也是必填但有的系统允许不选分类默认归到未分类。唯一性约束标题是否允许重复很多博客系统不做标题唯一校验但如果是 CMS 类系统标题重复会严重影响 SEO所以会做唯一性约束。长度限制标题上限是多少50 字、200 字还是 1000 字正文是大字段还是有限制标签最多传几个每个标签最长多少数据格式status 是 0/1 还是 0/1/2草稿/发布/回收站coverImage 是否必须是合法的 URLtags 是数组还是逗号拼接的字符串权限控制未登录能不能发文章非管理员能不能发文章普通用户发的文章状态是否强制为草稿这些规则不全是在接口文档里写明的我的做法是先看接口文档然后去联调环境用 Postman 手工试几个典型场景把开发实际实现的规则记下来再开始列用例。之前碰到过文档说 status 只有 0 和 1结果传 2 也能添加成功最后发现是后端的枚举校验漏了这种问题靠用例设计时多覆盖一步就能抓到。1.2 测试点如何整理成可执行用例接口规则理清之后把这些测试点拆成一条条可执行的用例。我习惯用如下几类来归类后面写自动化用例时思路会非常清晰用例类型关注点示例正常路径核心功能可用性填写全部合法参数断言添加成功并返回博客 ID参数必填校验缺少必填参数时的处理不传 title、不传 content、不传 categoryId参数格式校验类型、长度、格式不合法title 传数字类型、coverImage 不是合法 URL业务规则校验业务语义上的限制标题重复、分类不存在、未登录添加边界值参数临界点标题长度 0、1、上限-1、上限、上限1幂等性重复请求的影响同一请求连续提交两次是否产生两条相同文章核心思路就是正常能走通、异常有提示、边界不崩溃。用例设计阶段多花半小时后面写代码和排查问题能省半天时间。把这些测试点整理成表格后就可以进入下一阶段为每条用例准备测试数据。2. 测试数据准备与用例结构设计2.1 参数化与测试数据分层用例设计好了接下来要解决测试数据的问题。很多新手写接口用例喜欢把测试数据硬编码在代码里比如String title 测试文章; String content 测试内容;这样写单个用例没问题但用例一多就乱了。更关键的是参数校验类用例有几十种数据组合硬编码根本维护不动。所以我用数据驱动的方式来组织测试数据把用例数据和执行逻辑完全分离。我的数据分三层正常数据包含所有必填和选填字段值都取合法范围内的典型值比如标题用自动化测试文章_20250115正文用一段 100 字左右的文本。边界数据针对长度、数量等限制的边界值比如标题长度正好等于上限、上限减 1、上限加 1。异常数据缺失必填参数、传了错误类型、传了不存在的关联 ID、未携带 token 等。这三层数据分别对应不同的用例场景我会把它们统一维护在 Excel 或 JSON 文件里。我用 Excel 比较多因为表格看起来直观产品和开发也能看懂。每行对应一条用例字段包括用例编号、用例名称、请求参数JSON 字符串、预期状态码、预期业务码、预期返回信息、是否执行、备注。字段大概长这样用例编号用例名称请求参数预期状态码预期业务码预期信息BLOG_ADD_001添加博客-正常发布{title:测试文章,content:正文,categoryId:12,tags:[测试],status:1}200200添加成功BLOG_ADD_002添加博客-缺少标题{content:正文,categoryId:12,status:1}200400标题不能为空BLOG_ADD_003添加博客-标题超长{title:...201字,content:正文}200400标题长度超出限制这里有个细节很多系统的参数校验失败时HTTP 状态码依然返回 200而实际错误信息放在响应体里的业务码中。如果只按 HTTP 状态码来判断用例通过这类用例会永远通过但功能其实是错的。所以我在数据表里同时维护预期状态码和预期业务码两个字段断言时一起校验。2.2 正常场景、边界场景、异常场景的用例矩阵整理成用例矩阵后你会发现添加博客这个接口至少能拆出 25~30 条用例。我把完整的用例矩阵列出来供你参考实际项目里可以按需增删分类场景用例数关键断言点正常路径发布文章全部字段合法1HTTP 200、业务码 200、返回新 ID正常路径草稿文章status01返回成功状态为草稿正常路径仅必填字段可选字段不传1返回成功参数必填缺 title、缺 content、缺 categoryId3提示对应字段不能为空参数类型title 传数字、categoryId 传字符串、tags 传字符串3提示类型错误参数长度标题 0 字符、200 字符、201 字符、标签个数超限4边界成功或提示超长业务规则分类 ID 不存在、未登录、标题重复3提示分类不存在/未授权/标题已存在数据格式coverImage 传非法 URL、status 传非法值2提示格式错误幂等性连续提交两次相同请求1第二次被拦截或生成不同 ID取决于系统设计这个矩阵里的每条用例在框架里就是一行测试数据不需要为每条用例单独写代码。我要做的只是把执行逻辑写一次然后用不同数据驱动它跑 N 遍。3. 用例代码落地Java 版本核心实现3.1 依赖引入与用例基类封装我以 Java TestNG RestAssured 这套组合为例。如果你用的是 Python pytest requests思路完全一样就是语法层面的差异。先看 Maven 依赖dependencies dependency groupIdorg.testng/groupId artifactIdtestng/artifactId version7.8.0/version /dependency dependency groupIdio.rest-assured/groupId artifactIdrest-assured/artifactId version5.4.0/version /dependency dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId version2.16.1/version /dependency dependency groupIdorg.apache.poi/groupId artifactIdpoi-ooxml/artifactId version5.2.5/version /dependency /dependencies这里用到了 Jackson 做 JSON 序列化和反序列化用 POI 读 Excel 测试数据。RestAssured 的语法简洁断言也方便是 Java 接口自动化里用得比较多的 HTTP 客户端库。接下来封装一个操作博客接口的基类。这个类把通用逻辑读取配置、拼接 URL、设置请求头、发送请求都收敛起来用例里只关心传什么参数、断言什么结果public class BlogApi { private static final String BASE_URL ConfigReader.get(base.url); private static final String TOKEN LoginHelper.getToken(); private static final String ADD_BLOG_PATH /api/blog/add; public static Response addBlog(MapString, Object body) { return RestAssured.given() .baseUri(BASE_URL) .header(Content-Type, application/json) .header(Authorization, Bearer TOKEN) .body(body) .when() .post(ADD_BLOG_PATH) .then() .extract() .response(); } }TOKEN 为什么放在静态块里因为登录用例先执行获取到的 token 在 JVM 里是全局可用的后面所有用例都能直接复用。如果框架用例执行顺序不固定可以改成在BeforeSuite里统一登录并缓存 token。3.2 数据驱动读取与正向用例实现数据驱动的核心逻辑是用 TestNG 的DataProvider读取 Excel把每一行用例数据传给测试方法。先写一个 Excel 读取的工具方法public class ExcelDataProvider { DataProvider(name blogAddData) public Object[][] getBlogAddData(Method method) { ListTestCaseData testCases ExcelUtil.read(blog_add_cases.xlsx); Object[][] data new Object[testCases.size()][1]; for (int i 0; i testCases.size(); i) { data[i][0] testCases.get(i); } return data; } }这里的TestCaseData是自定义的数据封装类包含用例编号、用例名称、请求参数 JSON、预期状态码、预期业务码这几个字段。读取的时候把请求参数字符串解析成 Map其他字段原样保存。然后正用例就非常简洁了。整个添加博客的正向测试方法核心代码不足二十行Test(dataProvider blogAddData) public void testAddBlog(TestCaseData testCase) { MapString, Object requestBody JsonUtil.parseToMap(testCase.getRequestParams()); Response response BlogApi.addBlog(requestBody); Assert.assertEquals(response.getStatusCode(), testCase.getExpectedStatusCode(), HTTP状态码校验失败 testCase.getCaseName()); Assert.assertEquals(response.jsonPath().getInt(code), testCase.getExpectedBizCode(), 业务码校验失败 testCase.getCaseName()); Assert.assertNotNull(response.jsonPath().get(data.blogId), 返回数据中缺少blogId testCase.getCaseName()); }看到没有不管用例是正常场景还是异常场景这个方法都能跑。区别只在 Excel 里配置的预期结果不同。这就是数据驱动的威力代码只维护一份数据可以无限扩展。3.3 异常场景的断言细节异常场景的断言比正常场景要讲究。正常场景无非是返回成功 数据正确异常场景要断言的内容更多错误码对不对、错误信息是否准确、返回的数据结构是否正确。拿缺少标题这条用例来说很多系统的响应可能是这样的{ code: 400, message: 标题不能为空, data: null }但也有系统会返回字段级错误信息{ code: 400, message: 参数校验失败, data: { fieldErrors: [ {field: title, error: 标题不能为空} ] } }所以异常用例的断言要分两层第一层断言业务码是否正确第二层断言错误信息是否包含关键字。如果把错误信息也存进 Excel断言就变成了Assert.assertTrue(response.jsonPath().getString(message).contains(testCase.getExpectedMessage()), 错误信息不符合预期。实际信息 response.jsonPath().getString(message));这里有个坑错误信息的断言不能做全等匹配只能做包含匹配。因为后端返回的错误信息经常带动态内容比如标题长度不能超过 200 个字符当前长度为 201如果做全等匹配前端稍微改一个字用例就挂了但功能其实是正常的。包含匹配能避免这类导致误报的问题。4. 断言设计与结果校验不能只断言状态码4.1 分层断言策略很多做接口自动化的新手断言只写在响应层比如Assert.assertEquals(response.getStatusCode(), 200);然后用例执行完显示全绿实际数据根本没写进数据库接口是假成功。说得难听点这种用例不如不写因为它会给你虚假的安全感。我推荐的断言策略分三层HTTP 层状态码 200/201/400/401/403/500 是否正确。这个断言最基础能抓住网络链路、权限拦截、服务异常这类大问题。业务层响应体里的业务码code、消息message、数据data是否符合预期。这一层能抓住参数校验、业务规则校验、权限校验的逻辑错误。数据层关键字段是否落库落库的值是否正确。这一层能抓住接口返回成功但数据库没写入或者写入了但值不对这类深层问题。前两层是每个用例都要做的第三层则需要选择关键用例来做。对于添加博客来说正常添加这条用例必须做数据层断言参数校验类用例一般不需要因为后端校验失败时本来就不会写库。4.2 数据库层校验与用例隔离数据层校验的做法是在用例执行完后直接查数据库确认数据存在。我的实现思路是先在测试代码里执行一条 SQL查询刚添加的博客记录。public class BlogDbAssert { private static JdbcTemplate jdbcTemplate JdbcTemplateFactory.getInstance(); public static void assertBlogExists(Long blogId, String expectedTitle) { String sql SELECT id, title, status FROM blog WHERE id ?; MapString, Object row jdbcTemplate.queryForMap(sql, blogId); Assert.assertNotNull(row, 数据库中未查询到博客记录ID: blogId); Assert.assertEquals(row.get(title), expectedTitle, 数据库中的标题与预期不一致); Assert.assertEquals(row.get(status), 1, 数据库中的状态与预期不一致); } }执行数据库断言有个前提测试环境数据库对你开放查询权限。我所在的团队会把测试库的只读账号给到测试人员这样自动化用例不仅能断言接口响应还能直接验证数据落库发现问题的时候定位起来也更方便。数据层断言还有一个重要作用辅助用例隔离。添加博客用例每次执行都会产生一条新数据如果你跑的用例集包含标题重复这种场景第一次执行时数据不存在用例通过第二次执行时数据已经存在了用例就失败了。这就是典型的环境污染问题。解决思路有两个测试前后数据清理用例执行前记录当前博客总数执行后把新增的数据删除。动态数据名用例数据中的标题、标签等都拼上时间戳保证每次执行的数据都不一样。我的习惯是两个方案配合使用。正常添加用例用动态数据名避免干扰重复标题用例在BeforeMethod里先通过 SQL 插入一条固定数据执行完再清理干净。这样做的好处是数据可控、可重跑。5. 常见问题与排查实录5.1 典型问题速查表接口用例跑起来之后一定会遇到一堆网上搜不到答案的问题。我把添加博客用例里踩过的典型问题和排查思路整理成了一张速查表问题现象可能原因排查方向用例执行提示 401token 过期或未正确传递检查配置的 token 有效期确认请求头中 Authorization 的值中文乱码请求体编码格式不对检查 RestAssured 的编码设置统一使用 UTF-8数据库断言失败但接口返回成功接口写了缓存或事务未提交确认是否走了缓存必要时等待 1~2 秒再查库标题重复用例第二次执行失败前一次运行的数据没有清理在用例的前置步骤里先删除历史数据或使用动态名称接口返回成功但数据库无记录服务端逻辑是异步写入在断言前添加轮询等待轮询次数和间隔灵活配置时间字段断言不稳定时间格式包含毫秒或时区差异断言前统一格式化或只比对日期部分这里我重点说两个最容易踩的坑给你详细讲讲排查过程。第一个坑RestAssured 中文乱码。向接口提交含中文标题的请求体时服务端这边收到的全是乱码导致添加成功的是乱码数据数据库断言自然失败。排查时我先用 Postman 手动发同样的请求服务端收到的中文是正常的说明问题出在自动化请求上。最终定位是 RestAssured 默认字符集问题需要显式指定编码RestAssured.given() .config(RestAssured.config() .encoderConfig(EncoderConfig.encoderConfig() .defaultContentCharset(UTF-8) .defaultQueryParamCharset(UTF-8))) .baseUri(BASE_URL) .header(Content-Type, application/json; charsetUTF-8) .body(body) .when() .post(ADD_BLOG_PATH);第二个坑TestNG 数据驱动导致的执行顺序不稳定。添加博客用例正常路径、异常路径都在同一个 DataProvider 里TestNG 的默认执行顺序是按方法名排序的如果你在第一个方法里添加了一条固定标题的博客第二个方法又测重复标题就会因为顺序问题偶发失败。我的解决办法是给用例编号加前缀比如正常路径用BLOG_ADD_0xx异常路径用BLOG_ADD_1xx并且测试方法按编号排序执行。5.2 三条经验能帮你少走弯路再分享几个我在实际项目中磨出来的经验每条背后都有惨痛的教训。第一用例之间不要互相依赖。新手写接口用例容易犯一个毛病用例 A 添加博客用例 B 去修改用例 A 添加的博客用例 C 去删除。看着逻辑清晰但只要 A 挂了后面全挂排查问题时无从下手。正确做法是每个用例都独立准备数据B 需要的博客数据在 B 的前置步骤里自己创建执行完再清理。宁可多写几行前置代码也不要让用例产生隐式依赖。第二断言要精确到字段是否正确而不是返回是否成功。我之前接手过一个项目之前的同事断言只写了response.getStatusCode() 200用例全绿。后来我看数据发现添加博客的接口根本没写库只是返回了 200 空壳。从那以后我的添加类用例强制要求做一次数据库查询断言。没有数据层断言的接口用例等于没测。第三接口用例跑完务必清理数据否则环境会被测坏。特别是标题重复这类依赖唯一性约束的用例如果执行后不清理数据下次跑全量回归时必然失败。我在框架里专门写了一个AfterMethod的清理方法根据用例编号动态匹配清理逻辑。维护这个清理方法确实需要一些精力但换来的是用例可以无限次稳定重跑这笔账非常划算。这篇把添加博客的用例设计、数据准备、代码实现、断言策略和常见问题都过了一遍。基于这套思路你完全可以把同样的方法论迁移到修改博客删除博客查询博客列表等其他接口上核心逻辑都是一样的先梳理业务规则再设计用例矩阵用数据驱动实现用例最后把断言做到数据层。如果你在落地过程中遇到什么新问题欢迎随时交流我也在持续迭代这套框架。
返回列表