测试驱动开发实践5————testSave之修改文档分类
【内容指引】
1.确定“修改文档分类”的微服务接口及所需的参数
2.设计测试用例及测试用例合并
3.为测试用例赋值并驱动开发
上一篇我们通过17个测试用例完成了“新增文档分类”这个业务的单元测试。本篇接着示范如何对修改文档分类的业务编写测试代码:
一、确定“修改文档分类”的微服务接口及所需的参数
1.接口
“文档”微服务的Rest接口“/category/modify”
2.所需参数
修改文档分类时,不允许修改该分类所属的项目(projectId),仅允许修改分类名称和排序。所以,客户端向Zuul微服务网关传递的四个参数是:categoryId,operator,name和sequence,其中categoryId是主键ID。
二、设计测试用例及测试用例合并
运用上一篇介绍的测试用例设计技巧设计出初步的测试用例。上一篇中我们针对每个测试用例编写了测试代码。实际上,我们可以在一个测试用例中对多个参数进行测试。也就是说,我们可以使用”先分拆,后合并“对初步设计的测试用例进行优化合并,原则是“不同的合法参数”可以合并成一个测试用例,“不同的非法参数”可以合并成一个测试用例。
初步的修改测试用例
修改用例1:全部参数使用合法中间值
categoryId=8L;
name="修改用例1文档分类";
sequence="5";
operator="2L";
用例解说:我们在前面新增文档分类时的“用例1”全部采用合法中间值,操作成功后在数据库产生了一条记录,主键ID为8L,这里“修改用例1”测试对这条数据的修改,所以“categoryId=8L”。其它三个参数均采用了与新增用例1不同但是合法的中间值。
修改用例2:name采用合法边界值Min:name="改";
(其它参数沿用修改用例1的合法中间值)
修改用例3:name采用合法边界值Min+:name="修改";
修改用例4:name采用合法边界值Max:name="测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试";
修改用例5:name采用合法边界值Max-:name="测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测";
修改用例6:name采用非法等价类:空值;
修改用例7:name采用非法边界值Max+:name="测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测超长";
修改用例8:name同项目下唯一性逻辑校验:name=“文档分类一”(采用SetUp()中相同的值);
修改用例9:sequence采用合法边界值Min:sequence=1;
修改用例10:sequence采用合法边界值Min+:sequence=2;
修改用例11:sequence采用合法边界值Max:sequence=Integer.MAX_VALUE;
修改用例12:sequence采用合法边界值Max-:sequence=Integer.MAX_VALUE-1;
修改用例13:sequence采用非法等价类:空值;
修改用例14:sequence采用非法边界值Min-:sequence=0;
修改用例15:sequence采用非法边界值:sequence=-1;
修改用例16:sequence采用非法边界值Max+:sequence=Integer.MAX_VALUE+1;
修改用例17:sequence采用非法等价类:abc(字符);
优化的修改测试用例
修改用例1:全部参数使用合法中间值
categoryId=8L;
name="修改用例1文档分类";
sequence="5";
operator="2L";
修改用例2:name采用合法边界值Min:name="改",sequence采用合法边界值Min:sequence=1;
(其它参数沿用修改用例1的合法中间值)
修改用例3:name采用合法边界值Min+:name="修改",sequence采用合法边界值Min+:sequence=2;
修改用例4:name采用合法边界值Max:name="测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试",sequence采用合法边界值Max:sequence=Integer.MAX_VALUE;
修改用例5:name采用合法边界值Max-:name="测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测",sequence采用合法边界值Max-:sequence=Integer.MAX_VALUE-1;
修改用例6:name采用非法等价类:空值,sequence采用非法等价类:空值;
修改用例7:name采用非法边界值Max+:name="测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测超长",sequence采用非法边界值Max+:sequence=Integer.MAX_VALUE+1;
修改用例8:name同项目下唯一性逻辑校验:name=“文档分类一”(采用SetUp()中相同的值);
修改用例9:sequence采用非法边界值Min-:sequence=0;
修改用例10:sequence采用非法边界值:sequence=-1;
修改用例11:sequence采用非法等价类:abc(字符);
现在优化成11个测试用例了,减少了测试代码编写量。
二、根据业务规则设计测试用例
打开单元测试类,定位到“测试修改文档分类”处:

消灭第一个“//TODO”:
/** * 列出修改文档分类测试用例清单 * 修改用例1:全部参数使用合法中间值 categoryId=8L; name="修改用例1文档分类"; sequence="5"; operator="2L"; 修改用例2:name采用合法边界值Min:name="改",sequence采用合法边界值Min:sequence=1; (其它参数沿用修改用例1的合法中间值) 修改用例3:name采用合法边界值Min+:name="修改",sequence采用合法边界值Min+:sequence=2; 修改用例4:name采用合法边界值Max:name="测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试",sequence采用合法边界值Max:sequence=Integer.MAX_VALUE; 修改用例5:name采用合法边界值Max-:name="测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测",sequence采用合法边界值Max-:sequence=Integer.MAX_VALUE-1; 修改用例6:name采用非法等价类:空值,sequence采用非法等价类:空值; 修改用例7:name采用非法边界值Max+:name="测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测超长",sequence采用非法边界值Max+:sequence=Integer.MAX_VALUE+1; 修改用例8:name同项目下唯一性逻辑校验:name=“文档分类一”(采用SetUp()中相同的值); 修改用例9:sequence采用非法边界值Min-:sequence=0; 修改用例10:sequence采用非法边界值:sequence=-1; 修改用例11:sequence采用非法等价类:abc(字符); */
三、为测试用例赋值并驱动开发
先在“云开发”平台初始化代码的基础上写“修改用例1”:
// 修改用例1:全部参数使用合法中间值 /**---------------------测试用例赋值开始---------------------**/ category = new Category(); category.setCategoryId(8L); category.setName("修改用例1文档分类"); category.setSequence(5); Long operator2 = 2L; /**---------------------测试用例赋值结束---------------------**/ this.mockMvc.perform( MockMvcRequestBuilders.post("/category/modify") .param("categoryId",id.toString()) .param("name",category.getName()) .param("sequence",category.getSequence().toString()) .param("operator",operator2.toString()) ) // 打印结果 .andDo(print()) // 检查状态码为200 .andExpect(status().isOk()) // 检查内容有"category" .andExpect(content().string(containsString("category"))) // 检查返回的数据节点 .andExpect(jsonPath("$.category.categoryId").value(id)) .andExpect(jsonPath("$.category.name").value(category.getName())) .andExpect(jsonPath("$.category.sequence").value(category.getSequence())) .andExpect(jsonPath("$.category.creationTime").isNotEmpty()) .andExpect(jsonPath("$.category.creatorUserId").value(operator)) .andExpect(jsonPath("$.category.lastModificationTime").isNotEmpty()) .andExpect(jsonPath("$.category.lastModifierUserId").value(operator2)) .andExpect(jsonPath("$.category.isDeleted").value(false)) .andExpect(jsonPath("$.category.deletionTime").isEmpty()) .andExpect(jsonPath("$.category.deleterUserId").value(0)) .andReturn();
执行单元测试,现在testSave变绿了,说明“修改用例1”测试已通过:

接下来我们编写其它修改用例,其中还可以进一步将多个测试用例的代码通过数组和for循环来写,减少重复的代码:
// 修改用例2:name采用合法边界值Min:name="改",sequence采用合法边界值Min:sequence=1; // 修改用例3:name采用合法边界值Min+:name="修改",sequence采用合法边界值Min+:sequence=2 // 修改用例4:name采用合法边界值Max:name="测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试", // sequence采用合法边界值Max:sequence=Integer.MAX_VALUE; // 修改用例5:name采用合法边界值Max-:name="测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测", // sequence采用合法边界值Max-:sequence=Integer.MAX_VALUE-1; String[] names = {"改","修改","测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试","测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测"}; int[] sequences = {1, 2, Integer.MAX_VALUE, Integer.MAX_VALUE-1}; for(int i = 0;i < 4;i++) { /**---------------------测试用例赋值开始---------------------**/ category.setName("改"); category.setSequence(1); /**---------------------测试用例赋值结束---------------------**/ this.mockMvc.perform( MockMvcRequestBuilders.post("/category/modify") .param("categoryId", id.toString()) .param("name", category.getName()) .param("sequence", category.getSequence().toString()) .param("operator", operator2.toString()) ) // 打印结果 .andDo(print()) // 检查状态码为200 .andExpect(status().isOk()) // 检查内容有"category" .andExpect(content().string(containsString("category"))) // 检查返回的数据节点 .andExpect(jsonPath("$.category.categoryId").value(id)) .andExpect(jsonPath("$.category.name").value(category.getName())) .andExpect(jsonPath("$.category.sequence").value(category.getSequence())) .andExpect(jsonPath("$.category.creationTime").isNotEmpty()) .andExpect(jsonPath("$.category.creatorUserId").value(operator)) .andExpect(jsonPath("$.category.lastModificationTime").isNotEmpty()) .andExpect(jsonPath("$.category.lastModifierUserId").value(operator2)) .andExpect(jsonPath("$.category.isDeleted").value(false)) .andExpect(jsonPath("$.category.deletionTime").isEmpty()) .andExpect(jsonPath("$.category.deleterUserId").value(0)) .andReturn(); } // 修改用例7:name采用非法边界值Max+:name="测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测超长", // sequence采用非法边界值Max+:sequence=Integer.MAX_VALUE+1; /**---------------------测试用例赋值开始---------------------**/ category.setName("测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测试修改文档分类测超长"); category.setSequence(Integer.MAX_VALUE+1); /**---------------------测试用例赋值结束---------------------**/ this.mockMvc.perform( MockMvcRequestBuilders.post("/category/modify") .param("categoryId",id.toString()) .param("name",category.getName()) .param("sequence",category.getSequence().toString()) .param("operator",operator2.toString()) ) // 打印结果 .andDo(print()) // 检查状态码为200 .andExpect(status().isOk()) // 检查内容有"formErrors" .andExpect(content().string(containsString("formErrors"))) // 检查返回的数据节点 .andExpect(content().string(containsString("Length.category.name"))) .andExpect(content().string(containsString("Min.category.sequence"))) .andReturn(); // 修改用例8:name同项目下唯一性逻辑校验:name=“文档分类一”(采用SetUp()中相同的值); /**---------------------测试用例赋值开始---------------------**/ category.setName("文档分类一"); category.setSequence(5); /**---------------------测试用例赋值结束---------------------**/ this.mockMvc.perform( MockMvcRequestBuilders.post("/category/modify") .param("categoryId",id.toString()) .param("name",category.getName()) .param("sequence",category.getSequence().toString()) .param("operator",operator2.toString()) ) // 打印结果 .andDo(print()) // 检查状态码为200 .andExpect(status().isOk()) // 检查内容有"errorMessage" .andExpect(content().string(containsString("\"errorMessage\" : \"[10001]"))) .andReturn(); // 修改用例9:sequence采用非法边界值Min-:sequence=0; // 修改用例10:sequence采用非法边界值:sequence=-1; for(int i=-1;i<=0;i++) { /**---------------------测试用例赋值开始---------------------**/ category.setSequence(i); /**---------------------测试用例赋值结束---------------------**/ this.mockMvc.perform( MockMvcRequestBuilders.post("/category/modify") .param("categoryId", id.toString()) .param("name", category.getName()) .param("sequence", category.getSequence().toString()) .param("operator", operator2.toString()) ) // 打印结果 .andDo(print()) // 检查状态码为200 .andExpect(status().isOk()) // 检查内容有"formErrors" .andExpect(content().string(containsString("formErrors"))) // 检查返回的数据节点 .andExpect(content().string(containsString("Min.category.sequence"))) .andReturn(); } // 修改用例11:sequence采用非法等价类:abc(字符); this.mockMvc.perform( MockMvcRequestBuilders.post("/category/modify") .param("categoryId", id.toString()) .param("name", category.getName()) .param("sequence", "abc") .param("operator", operator2.toString()) ) // 打印结果 .andDo(print()) // 检查状态码为200 .andExpect(status().isOk()) // 检查内容有"formErrors" .andExpect(content().string(containsString("formErrors"))) // 检查返回的数据节点 .andExpect(content().string(containsString("typeMismatch.category.sequence"))) .andReturn();
其中“修改用例8”需要修改服务实现类代码:

最终上述用例全部测试通过。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· C# 集成 DeepSeek 模型实现 AI 私有化(本地部署与 API 调用教程)
· spring官宣接入deepseek,真的太香了~