《软件测试项目实践任务书》
章节一至四(单元测试):软件测试和质量管理——课设1-CSDN博客
章节五(功能测试):软件测试和质量管理——课设2-CSDN博客
章节六(性能测试):软件测试和质量管理——课设3-CSDN博客
章节七(安全测试)至八:https://blog.csdn.net/qq_53229521/article/details/137379060?spm=1001.2014.3001.5502
【目的】
通过本课程的学习,要求学生掌握软件测试的基本概念、基本理论,能够根据测试任务,制定测试计划,设计测试用例集,搭建测试环境,完成单元测试、功能测试、性能测试、安全测试,获取测试结果并总结,撰写缺陷报告和综合测试报告,达到本课程的课程目标。
课程目标1:了解软件测试应用领域的相关科技文化、科技发展动态,增强“科技强国”的担当意识;认识信息化、智能化、自动化在软件测试技术发展中应用和体现,感知科学技术是第一生产力在软件测试技术中的体现;在锻炼实践动手能力的过程中培养创新意识。
课程目标2:学习软件测试的基本概念与基本原理,熟悉软件质量管理的基本概念和实施过程,理解软件测试在工程应用中的实践意义。运用测试基础知识和理论,分析测试需求、制定测试方案和测试计划、设计测试用例、搭建测试环境,构建出切实有效的实验方案。了解软件测试前沿技术,增强“科技强国”的担当意识。
课程目标3:针对用户特定的测试要求,分析测试环境,构建测试条件、组织和实施测试活动,能综合运用软件测试的理论、方法分析和解决工程测试问题。各种自动化的测试工具使用,认识信息化、智能化、自动化在软件测试技术发展中应用和体现,感知科学技术是第一生产力在软件测试技术中的体现。
课程目标4:能够独立完成本课程的设计内容,分析和总结实验过程,撰写缺陷报告并反馈到复杂工程的设计实践中,提高自身独立分析和解决实际问题的能力,在锻炼实践动手能力的过程中具备刻苦钻研的工匠精神。
【要求】
本次课程设计各自独立完成jeeSite项目指定类的单元测试、指定模块的功能测试、性能测试和安全测试,整个过程按照需求分析—>测试计划—>用例设计—>测试执行—>测试报告的流程进行,具体内容和要求参考指导书。测试的web站点为登录 - Software,登录账户为学号,密码默认为123456,可自行修改。课程考核总成绩按平时成绩*10%+阶段成果*50%+总结报告*20%+答辩测试*20%来评分,务必按照进度及时提交所需材料。
【参考资料】
1、《软件测试实验教程》,朱少明等,清华大学出版社,2019.6
2、JeeSite 快速开发平台,https://jeesite.com/docs/install-deploy/
3、软件测试项目实战,软件测试项目实战【不爱听书】测试全套教程以及源码_软件测试项目实战教程-CSDN博客
【工作内容及工作计划】
时间 | 工作内容 | 是否 完成 | 未完成的原因 | 在哪些方面 做了改进 |
2023-06-13 至 2023-06-14 | 项目需求分析、项目测试计划、单元测试 撰写报告 | 是 | / | 根据测试结果分析所测方法存在的缺陷,并给出修改意见 |
2023-06-15 至 2023-06-16 | 功能测试 撰写报告 | 是 | / | 在测试过程中发现系统存在的功能缺陷,并给出改进方案 |
2023-06-19 | 性能测试 撰写报告 | 是 | / | 根据测试后得到的数据结果发现系统存在的性能缺陷,并给出改进方案 |
2023-06-20 至 2023-06-21 | 安全测试 撰写报告 答辩 | 是 | / | 学习了Fuzz的使用,增加了Sql注入和暴力破解密码的测试 |
软件测试项目实践报告
一、项目介绍
1、项目背景
JeeSite是一款基于JavaEE和Spring Framework开发的开源企业级快速开发平台,具有权限管理、代码生成、内容管理、报表等模块,是一种突破传统开发模式,定制化需求,快速搭建企业级应用的解决方案。其使用Maven做项目管理,提高项目的易开发性、扩展性。
本项目旨在对JeeSite平台进行全面的软件测试,包括:单元测试、功能测试、性能测试和安全测试,发现平台潜在的缺陷、提升软件质量、确保系统稳定性和可靠性。
2、项目目的
- 通过对测试结果的分析,得到对软件质量的评价;
- 发现JeeSite存在的缺陷,为修复bug提供建议,提升系统质量和可靠性;
- 确保JeeSite平台系统的稳定性、兼容性、易用性和安全性;
- 提高软件测试人员的测试技能和经验,加强软件测试的基本概念和理论。
3、系统结构图
用户进入平台需进行账号密码登录,平台提供忘记密码功能。
用户登陆成功后进入主页面,平台会显示使用率、关注数、因特网、新用户的数量,并以图表形式呈现月度报表、销售图表、报表等信息。
平台内设我的工作、内容管理、个人中心三大模块。其中,我的工作中可以查看和发布站内消息;个人中心可以修改个人信息、修改密码、退出登录、切换角色;内容管理模块包含内容发布、栏目管理、站点设置、模板管理四个子模块。
其中,内容发布模块实现对文章的查询、修改、停用、删除、预览、添加功能;栏目管理模块实现对栏目的查询、编辑、停用、删除、访问、添加和新增下级栏目功能;站点设置模块实现对站点的查询、编辑、停用、删除、浏览、增加功能;模板管理模块实现对内容的查看、模板文件名的修改、模板代码的修改。
具体的系统结构图如下图1-1所示。
图1-1 系统结构图
二、需求分析
JeeSite是一个为企业提供信息管理系统的基础框架平台。现对该项目进行需求分析,提炼出相关测试点,更好地指导测试流程和测试目标。
2.1 功能需求
用户进入平台需进行账号密码登录,登陆成功后进入主页面,平台内设我的工作、内容管理、个人中心三大模块。其中,我的工作中可以查看和发布站内消息,个人中心可以修改个人信息、修改密码、退出登录、切换角色。
本项目仅针对JeeSite平台的内容管理模块进行功能测试。其具有内容发布、栏目管理、站点设置、模板管理四个子模块,我们需要对各个功能模块进行测试。
2.1.1 内容发布模块
文章列表:①可根据栏目编码、内容标题(模糊)、关键字、文档状态查询文章;②可点击栏目列查询出在该栏目的文章;③可点击文章标题查看/修改文档;④可对文章进行编辑、停用、删除、预览、启用;④排序文章。
添加文章:①可设置文章关键字、权重、推荐位、发布时间、外部链接等信息;②提供可视化内容在线编辑器;③支持图片、Flash上传及添加附件,提供文件增删管理。
2.1.2 栏目管理模块
栏目列表:① 可根据栏目名称(模糊)、模块类型、状态、备注信息查询栏目;②可点击栏目行编辑栏目、删除栏目、停用栏目、新增下级栏目表、访问栏目、启用栏目;③可点击栏目名称查看/修改栏目;④展开/折叠/刷新栏目表。
添加栏目:①可设置文章关键字、权重、推荐位、发布时间、外部链接等基本信息;②提供可视化内容在线编辑器;③支持图片上传及添加附件功能。
2.1.3 站点设置模块
站点列表:① 可根据站点名称(模糊)、站点标题(模糊)、状态域名、状态查询站点;②可点击站点行编辑、删除、停用、浏览、启用站点;③可点击栏目名称查看/修改站点;④排序站点。
添加站点:①可设置站点名称、编码、域名、排序、标题、logo等基本信息;②提供可视化内容在线编辑器;③支持图片、Flash上传及添加附件,提供文件增删管理。
2.1.4 模板管理模块
模板目录:①可点击模板列查看模板内容;②进入模板后可修改模板文件名;③进入模板后可修改模板代码。
综上,功能需求如下表2-1所示。在测试过程中,要确保每个功能模块的实现符合《内容管理模块功能说明》文档中所描述的要求,同时也要挖掘一些隐藏的功能需求,以确保系统的完整性和可靠性。
表2-1 功能需求表
模块 | 功能需求 | |
登录模块 | 用户登录、忘记密码 | |
内 容 管 理 模 块 | 内容发布 | 查询文章、修改文章、删除文章、停用文章、预览文章、新增文章、切换站点、启用文章、排序文章、访问网站 |
栏目管理 | 查询栏目、编辑栏目、删除栏目、停用栏目、访问栏目、新增栏目、新增下级栏目、启用栏目、排序栏目、切换站点、展开栏目、折叠栏目、刷新栏目 | |
站点设置 | 查询站点、编辑站点、删除站点、停用站点、访问站点、新增站点、启用站点、排序站点 | |
模板管理 | 查看模板内容、修改模板文件名、修改模板代码 |
2.2 数据需求
由于仅针对JeeSite平台的内容管理模块进行功能测试,现对于进行数据需求分析。在登录的情况下才可以进行内容管理,故还需要考虑登录模块。
下表2-2是对于JeeSite平台内容管理模块测试的数据需求。
表2-2 数据需求表
模块 | 数据需求 | ||
登录模块 | 账号、密码 | ||
内 容 管 理 模 块 | 内容 发布 | 切换站点 | 选择站点名称 |
查询文章 | 输1或多:栏目编码、来源、内容标题、关键字、状态 | ||
新增文章 /修改文章 | 必填数据:所属栏目、内容标题、正文 | ||
选填数据:外部链接、权重、权重过期日期、摘要、内容图片、关键字、自定义内容视图、视图参数配置、备注信息、拓展字段 | |||
删除文章 | / | ||
停用文章 | / | ||
启用文章 | / | ||
预览文章 | / | ||
排序文章 | / | ||
访问网站 | / | ||
栏目 管理 | 切换站点 | 选择站点名称 | |
查询栏目 | 输1或多:栏目名称、模块类型、状态、备注信息 | ||
新增栏目 /修改栏目 /新增下级栏目 | 必填数据:栏目名称 | ||
默认已有数据:栏目编码、归属站点、内容模型、排序号、是否在导航中显示、是否允许评论、是否在分类页中显示、是否需要审核、内容展现模式 | |||
选填数据:上级栏目、栏目图片、外部连接、超链接目标窗口、栏目描述信息、栏目关键字、自定义列表视图、自定义内容视图、视图参数配置、备注信息、拓展字段 | |||
删除栏目 | / | ||
启用栏目 | / | ||
停用栏目 | / | ||
访问栏目 | / | ||
展开/折叠 /刷新栏目 | / | ||
站点 设置 | 查询站点 | 填1或多:站点名称、站点标题、站点域名、状态 | |
新增站点 | 必填数据:站点名称、站点编码、站点标题、版权信息 | ||
默认已有数据信息栏:主题风格、首页视图 | |||
选填数据:站点域名、站点排序、站点logo、描述、关键字、备注信息 | |||
修改站点 | 必填数据:站点名称、站点标题、版权信息 | ||
默认已有数据:主题风格、首页视图 | |||
不可修改数据:站点编码 | |||
选填数据:站点域名、站点排序、站点logo、描述、关键字、备注信息 | |||
删除站点 | / | ||
停用站点 | / | ||
启用站点 | / | ||
访问站点 | / | ||
排序站点 | / | ||
模板 管理 | 查看内容 | / | |
修改文件名 | 文件名 | ||
修改代码 | 代码 |
2.3 性能需求
JeeSite平台需要支持高并发、高负载的访问需求,因此需要对系统的性能进行测试。测试过程中,需要模拟多个用户同时访问的情况,并对系统的响应时间、吞吐量等指标进行监控和评估,以确定系统是否运行正常、内存正常、系统稳定,是否满足性能需求。
2.4 安全需求
JeeSite平台需要保证用户信息和系统数据的安全性和保密性,因此需要进行安全性测试。测试过程中,需要判断用户密码是否加密显示,保障用户的私人信息不被窃取;模拟黑客攻击等情况,以确定系统是否存在安全漏洞并对系统进行安全性评估。
2.5 易用性需求
JeeSite平台需要保证用户操作简单、方便,因此需要进行易用性测试。测试过程中,需要评估用户体验,测试各个功能模块的可操作性,即是否符合常见标准与规范,用户操作是否方便舒适;评估用户界面的易用性,即界面控件是否正常使用,布局、排版是否合理。
2.6 兼容性需求
JeeSite平台需要支持多种浏览器和操作系统环境,因此需要进行兼容性测试。测试过程中,需要测试系统在不同操作系统和浏览器下的兼容性,用户数据在后台存储时互不影响,以保证系统能够在不同环境下正常运行。
三、测试计划
3.1 测试内容
根据测试任务,制定测试计划,对JeeSite平台进行单元测试、功能测试、性能测试和安全测试。具体包括:设计测试用例集,搭建测试环境,编写测试代码,获取测试结果并分析总结,撰写缺陷报告和综合测试报告。
3.2 具体计划
表3-1 测试计划表
测试软件 | JeeSite平台 | |
测试目的 | ① 测试软件实现代码的部分方法是否存在缺陷和错误; ② 测试功能实现是否正常,是否符合用户需求和软件需求; ③ 测试多用户使用平台时的情况,是否满足用户的性能需求; ④ 测试是否保证用户信息和系统数据的安全性和保密性; ⑤ 并对软件的易用性、兼容性进行测试及分析。 | |
测试前提 | 测试工具安装完成,环境配置成功,系统正常运行 | |
测试范围 | 整个系统 | |
测试方式 | 手工测试 + 自动化测试(编写自动化脚本selenium) | |
测试模块 | 单元测试 | 测试StringUtils类和DateUtils类的5个方法 |
功能测试 | 测试内容管理模块的功能 | |
性能测试 | 模拟多用户使用平台,测试系统响应时间、吞吐量等性能指标 | |
安全测试 | 模拟平台受到网络等攻击,测试系统安全性能 |
3.3 测试环境及配置
表3-2 测试环境及配置
用途 | 工具 |
操作系统 | Windows 10 |
开发工具 | Eclipse 4.26.0 |
浏览器 | Chrome 114.0.5735.111 |
单元测试框架 | JUnit 5.0 |
功能测试框架 | Selenium IDE 插件 3.17.2、Chrome Driver |
性能测试框架 | Jmeter 5.5、Firefox浏览器 |
安全测试工具 | OWASP ZAP 2.12.0、Firefox浏览器 |
四、单元测试
4.1 用例设计
单元测试就是针对最小的功能单元编写测试代码,Java程序中最小的功能单元是方法,因此单元测试就是针对Java方法的测试,进而检查方法的正确性,这里运用Junit框架进行测试,导入依赖后进行测试。
对于每个方法,首先分析代码进行等价类划分;然后设计有效等价类测试用例,使其尽可能多地覆盖尚未覆盖的有效等价类,重复这一步骤,直到所有有效等价类均被测试用例所覆盖;再设计无效等价类测试用例,使其每个用例只覆盖一个无效等价类,重复这一步骤直到所有无效等价类被覆盖。
4.1.1 StringUtils类
(1)htmlAbbr() 方法
该方法实现缩略字符串(适应于与HTML标签)的功能。
其输入是一个字符串param和一个整数length,输出是一个不超过length的截取后的字符串。
在截取的过程中,先对param字符串中的HTML标签和特殊字符进行去除,然后计算处理后的字符串长度,将它与length比较来进行截取或补全。最后,将去除的HTML标签和特殊字符还原回来并输出。
① 划分等价类并编号
表4-1 String htmlAbbr()方法的等价类表
输入数据 | 有效等价类 | 无效等价类 |
目标字符串 (param) |
|
|
截取长度 (length) |
|
|
② 为有效等价类设计测试用例
表4-2 String htmlAbbr()方法的有效等价类测试用例表
序号 | 测试用例描述 | 输入参数 | 期望输出 | 覆盖 范围 | |
param | length | ||||
1 | 有效等价类 | "<html><head><title>Test Page</title></head><body><h1>Test Heading</h1><p>This is a test paragraph.</p></body></html>" | 20 | <html><head><title>Test Page</title></head><body><h1>Test Hea...</h> | 1,7 |
2 | 有效等价类 | "“Hello, Rql!” A girl" | 10 | “Hello,... | 2,7 |
3 | 有效等价类 | "Hello, Rql! A girl love eat." | 5 | He... | 3,7 |
4 | 有效等价类 | "Hello, Rql! A girl love eat." | 40 | "Hello, Rql! A girl love eat." | 3,6 |
③ 为无效等价类设计测试用例
表4-3 String htmlAbbr()方法的无效等价类测试用例表
序号 | 测试 用例描述 | 输入参数 | 期望输出 | 覆盖 范围 | |
param | length | ||||
5 | 无效等价类 | 空字符串 | 20 | 空字符串 | 4,6 |
6 | 无效等价类 | null | 10 | 空字符串 | 5,6 |
7 | 无效等价类 | "<html><head><title>Test Page</title></head><body><h1>Test Heading</h1><p>This is a test paragraph.</p></body></html>" | -3 | ... | 1,8 |
8 | 无效等价类 | "<html><head><title>Test Page</title></head><body><h1>Test Heading</h1><p>This is a test paragraph.</p></body></html>" | rql | 抛出异常 | 1,9 |
(2)InString() 方法
该方法实现一个忽略大小写的字符串匹配的功能。
其输入是两个参数str和strs,其中str表示要查找的字符串,strs表示查找的目标字符串集合。其返回值为一个布尔值,表示str是否在strs中。
具体实现思路为:首先判断str和strs是否为null,若不是则使用for循环遍历参数strs中的所有元素。对于每个元素,先调用trim方法去除字符串两端的空格,然后使用equalsIgnoreCase方法忽略大小写地比较s和str是否相同。若相同则返回true;否则继续循环查找。当遍历完整个strs仍未找到匹配项,返回false,表示未找到。
① 划分等价类并编号
表4-4 String InString()方法的等价类表
输入数据 | 有效等价类 | 无效等价类 |
验证字符串 (str) |
|
|
字符串组 (strs) |
|
|
② 为有效等价类设计测试用例
表4-5 String InString()方法的有效等价类测试用例表
序号 | 测试用例 描述 | 输入参数 | 期望 输出 | 覆盖 范围 | |
str | strs | ||||
1 | 有效等价类 | "RuanQiongLu!" | "ABCD", "Rql", "hello!" | false | 1,5 |
2 | 有效等价类 | "" | "ABCD", "Rql", "" | true | 2,4 |
③ 为无效等价类设计测试用例
表4-6 String InString()方法的无效等价类测试用例表
序号 | 测试用例 描述 | 输入参数 | 期望 输出 | 覆盖 范围 | |
str | strs | ||||
3 | 无效等价类 | null | "ABCD", "Rql", "hello!" | false | 3,5 |
4 | 无效等价类 | "Rql!" | null | fasle | 1,6 |
5 | 无效等价类 | null | "ABCD", "Rql", null | false | 3,7 |
(3)uncamelCase() 方法
该方法实现一个驼峰式字符串转换为下划线式字符串的功能。即:若输入字符串camelStr,该方法将返回一个下划线式字符串camel_str。
其输入是字符串s。首先判断s是否为null,若为null则返回null;不为null则创建一个StringBuilder实例存放转换结果,并设置一个boolean变量upperCase,表示当前字符是否为大写字符(默认为false)。然后使用for循环遍历输入字符串中的每个字符。在每次循环中,首先获取当前字符,然后定义一个boolean变量nextUpperCase,表示下一个字符是否为大写字符(默认值为true)。如果当前字符不是字符串的首字符,就判断当前字符是否为大写字符。如果是,则判断前一次循环中当前字符是否为大写字符或下一个字符是否为大写字符。如果不是,则向结果字符串中添加一个下划线符号。最后,将当前字符转换成小写字母并添加到结果字符串中。
① 划分等价类并编号
表4-7 uncamelCase()方法的等价类表
输入数据 | 有效等价类 | 无效等价类 |
字符串 (s) |
|
|
② 为有效等价类设计测试用例
表4-8 uncamelCase()方法的有效等价类测试用例表
序号 | 测试用例描述 | 输入参数s | 期望输出 | 覆盖范围 |
1 | 有效等价类 | "ThisIsRql" | "this_is_rql" | 1,2 |
2 | 有效等价类 | "THISIsRQL" | "t_his_is_rql" | 3,4 |
3 | 有效等价类 | "this_is_rql" | "this_is_rql" | 5 |
③ 为无效等价类设计测试用例
表4-9 uncamelCase()方法的无效等价类测试用例表
序号 | 测试用例描述 | 输入参数s | 期望输出 | 覆盖范围 |
4 | 无效等价类 | null | null | 6 |
5 | 无效等价类 | 空字符串 | 空字符串 | 7 |
6 | 无效等价类 | 空格 | 空格 | 8 |
4.1.2 DateUtils类
(1)getMonthHasDays()方法
该方法实现一个获取月份天数的方法。
输入包含年和月的字符串,格式为yyyy-MM。输出该月份的总天数。
具体实现方法:首先使用FastDateFormat类中的getInstance()方法获取日期格式对象来格式化传入的日期参数,然后使用字符串截取方法将格式化后的字符串切分成年月的两个子字符串。接着按照历法规则计算该月份的天数,若为大月3则返回31,若为小月则返回30,若为平年2月则返回28,若为闰年2月则返回29。
① 划分等价类并编号
表4-10 getMonthHasDays()方法的等价类表
输入数据 | 有效等价类 | 无效等价类 | |
日期(date) | 年(year) |
|
|
月(month) |
|
|
② 为有效等价类设计测试用例
表4-11 getMonthHasDays()方法的有效等价类测试用例表
序号 | 测试用例 描述 | 输入参数 | 期望输出 | 覆盖 范围 | |
Year | Month | ||||
1 | 有效等价类 | 2023 | 6 | 30 | 2,5 |
2 | 有效等价类 | 2023 | 7 | 31 | 2,6 |
3 | 有效等价类 | 2023 | 2 | 28 | 2,7 |
4 | 有效等价类 | 2024 | 2 | 29 | 1,7 |
③ 为无效等价类设计测试用例
表4-12 getMonthHasDays()方法的无效等价类测试用例表
序号 | 测试用例 描述 | 输入参数 | 期望输出 | 覆盖无效等价类范围 | |
Year | Month | ||||
5 | 无效等价类 | null | 6 | 抛出异常 | 3 |
6 | 无效等价类 | rql | 6 | 抛出异常 | 4 |
7 | 无效等价类 | 2023 | 0 | 抛出异常 | 8 |
8 | 无效等价类 | 2023 | 13 | 抛出异常 | 9 |
9 | 无效等价类 | 2023 | null | 抛出异常 | 10 |
10 | 无效等价类 | 2023 | rql | 抛出异常 | 11 |
(2)ParseDateBetweenString()方法
该方法实现将字符串解析成日期区间的功能。
该方法输入一个字符串,包含两个日期,格式为yyyy-MM-dd,中间用“~”连接。输出一个Date数组,包含上述两个日期,分别是起始日期和结束日期。
具体实现方法:首先,两个Date类型的变量beginDate和endDate初始化为null。然后,使用isNotBlank()方法判断传入的日期字符串参数dateString是否为空白。若不为空白,则使用split()方法,将日期字符串dateString按照“~”进行分割,分成两个日期字符串begin和end。接着,再次使用isNoneBlank()方法判断begin和end是否都不为空白。若都不为空白,则使用parseDate()方法将begin和end字符串解析为对应的Date对象,并分别赋值给beginDate和endDate。最后,使用new Date[]{beginDate, endDate}创建一个长度为2的Date数组,包含beginDate和endDate,将其作为方法的返回值。
① 划分等价类并编号
表4-13 ParseDateBetweenString()方法的等价类表
有效等价类 | 无效等价类 | ||
日期1 (date1) | 年1 (year1) |
|
|
月1 (month1) |
|
| |
日 (day1) |
|
| |
连接符(connect) |
|
| |
日期2 (date2) | 年2 (year2) |
|
|
| |||
月2 (month2) |
|
| |
| |||
日2 (day2) |
|
| |
|
② 为有效等价类设计测试用例
表4-14 ParseDateBetweenString()方法的有效等价类测试用例表
序 号 | 测试用 例描述 | 输入参数 | 期望输出 | 覆盖有效等价类范围 | ||
date1 | connect | date2 | ||||
1 | 有效等价类 | 2024-02-29 | ~ | 2024-11-30 | ["2024-02-29", "2024-11-30"] | 1,7,13,23,25,30,32,39,43,53 |
2 | 有效等价类 | 2023- 07- 31 | ~ | 2025- 02- 28 | ["2023-07-31", "2025-02-28"] | 2,5,15,23,26,30,33,40,41,54 |
3 | 有效等价类 | 2023-04-30 | ~ | 2023-05-31 | ["2023-04-30", "2023-05-31"] | 2,6,14,23,26,30,31,39,44,53 |
4 | 有效等价类 | 2023-02-28 | ~ | 2024-02-29 | ["2023-02-28", "2024-02-29"] | 2,7,12,23,25,30,33,39,42,53 |
③ 为无效等价类设计测试用例
表4-15 ParseDateBetweenString()方法的无效等价类测试用例表
序 号 | 测试用 例描述 | 输入参数 | 期望输出 | 覆盖无效 等价类范围 | ||
date1 | connect | date2 | ||||
5 | 无效等价类 | null-06-13 | ~ | 2024-02-29 | [null, "2024-02-29"] | 3 |
6 | 无效等价类 | rql-01-01 | ~ | 2024-02-29 | [null, "2024-02-29"] | 4 |
7 | 无效等价类 | 2023-00-01 | ~ | 2024-02-29 | 抛出异常 | 8 |
8 | 无效等价类 | 2023-13-01 | ~ | 2024-02-29 | 抛出异常 | 9 |
9 | 无效等价类 | 2023-null-01 | ~ | 2024-02-29 | [null, "2024-11-30"] | 10 |
10 | 无效等价类 | 2023-Rql-01 | ~ | 2024-02-29 | [null, "2024-11-30"] | 11 |
11 | 无效等价类 | 2023-06-00 | ~ | 2024-02-29 | 抛出异常 | 16 |
12 | 无效等价类 | 2023-02-29 | ~ | 2024-02-29 | 抛出异常 | 17 |
13 | 无效等价类 | 2024-02-30 | ~ | 2024-02-29 | 抛出异常 | 18 |
14 | 无效等价类 | 2023-06-31 | ~ | 2024-02-29 | 抛出异常 | 19 |
15 | 无效等价类 | 2023-07-32 | ~ | 2024-02-29 | 抛出异常 | 20 |
16 | 无效等价类 | 2023-06-null | ~ | 2024-02-29 | [null, "2024-02-29"] | 21 |
17 | 无效等价类 | 2023-06-rql | ~ | 2024-02-29 | [null, "2024-02-29"] | 22 |
18 | 无效等价类 | 2023-06-13 | 至 | 2024-02-29 | [null, null] | 24 |
19 | 无效等价类 | 2023-06-13 | ~ | 2022-02-28 | 抛出异常 | 27 |
20 | 无效等价类 | 2023-06-13 | ~ | null-02-28 | ["2023-06-13", null] | 28 |
21 | 无效等价类 | 2023-06-13 | ~ | rql-02-28 | ["2023-06-13", null] | 29 |
22 | 无效等价类 | 2023-06-13 | ~ | 2024-00-01 | 抛出异常 | 34 |
23 | 无效等价类 | 2023-06-13 | ~ | 2024-13-01 | 抛出异常 | 35 |
24 | 无效等价类 | 2023-06-13 | ~ | 2024-null-01 | ["2023-06-13", null] | 36 |
25 | 无效等价类 | 2023-06-13 | ~ | 2024-rql-01 | ["2023-06-13", null] | 37 |
26 | 无效等价类 | 2023-06-13 | ~ | 2023-05-01 | 抛出异常 | 38 |
27 | 无效等价类 | 2023-06-13 | ~ | 2023-07-00 | 抛出异常 | 45 |
28 | 无效等价类 | 2023-06-13 | ~ | 2025-02-29 | 抛出异常 | 46 |
29 | 无效等价类 | 2023-06-13 | ~ | 2024-02-30 | 抛出异常 | 47 |
30 | 无效等价类 | 2023-06-13 | ~ | 2023-06-31 | 抛出异常 | 48 |
31 | 无效等价类 | 2023-06-13 | ~ | 2023-07-32 | 抛出异常 | 49 |
32 | 无效等价类 | 2023-06-13 | ~ | 2023-07-null | ["2023-06-13", null] | 50 |
33 | 无效等价类 | 2023-06-13 | ~ | 2023-07-rql | ["2023-06-13", null] | 51 |
34 | 无效等价类 | 2023-06-13 | ~ | 2023-06-10 | 抛出异常 | 52 |
4.2 测试执行
使用junit5测试工具,利用等价分类法设计测试用例,根据测试用例编写测试代码。
4.2.1 代码
(1)测试htmlAbbr()
// 测试String htmlAbbr():缩略字符串(适应于与HTML标签的)
@Test
public void testHtmlAbbr() throws UnsupportedEncodingException {
StringUtils StringUtils = new StringUtils();
// 测试1:包含HTML标记的字符串
String input_1 = "<html><head><title>Test Page</title></head><body><h1>Test Heading</h1><p>This is a test paragraph.</p></body></html>";
String expected_1 = "<html><head><title>Test Page</title></head><body><h1>Test Hea...</h>";
String actual_1 = StringUtils.htmlAbbr(input_1, 20);
Assert.assertEquals(expected_1, actual_1);
// 测试2:包含HTML特殊字符
String input_2 = "“Hello, Rql!” A girl";
String expected_2 = "“Hello,...";
String actual_2 = StringUtils.htmlAbbr(input_2, 10);
Assert.assertEquals(expected_2, actual_2);
// 测试3:纯字符串
String input_3 = "Hello, Rql! A girl love eat.";
String expected_3 = "He...";
String actual_3 = StringUtils.htmlAbbr(input_3, 5);
Assert.assertEquals(expected_3, actual_3);
// 测试4:取值大于字符串长度
String input_4 = "Hello, Rql! A girl love eat.";
String expected_4 = "Hello, Rql! A girl love eat.";
String actual_4 = StringUtils.htmlAbbr(input_4, 40);
Assert.assertEquals(expected_4, actual_4);
// 测试5:空字符串
String emptyInput = "";
String emptyExpected = "";
String emptyActual = StringUtils.htmlAbbr(emptyInput, 20);
Assert.assertEquals(emptyExpected, emptyActual);
// 测试6:null字符串
String nullInput = null;
String nullExpected = "";
String nullActual = StringUtils.htmlAbbr(nullInput, 10);
Assert.assertEquals(nullExpected, nullActual);
// 测试7:所取长度非正数
String input_7 = "<html><head><title>Test Page</title></head><body><h1>Test Heading</h1><p>This is a test paragraph.</p></body></html>";
String expected_7 = "...";
String actual_7 = StringUtils.htmlAbbr(input_7, -3);
Assert.assertEquals(expected_7, actual_7);
// 测试8:所取长度非数串
// String actual_8 = StringUtils.htmlAbbr(input_7, rql);
}
(2)测试InString()
// 测试InString(str, strs):strs是否包含字符串str
@Test
public void testInString(){
StringUtils StringUtils = new StringUtils();
// 测试1:均为非null且找不到
String str_1 = "RuanQiongLu!";
String[] strs_1 = new String[] {"ABCD", "Rql", "hello!"};
Assert.assertFalse(StringUtils.inStringIgnoreCase(str_1, strs_1));
// 测试2:均为非null且找到
String str_2 = "";
String[] strs_2 = new String[] {"ABCD", "Rql", "hello!", ""};
Assert.assertTrue(StringUtils.inStringIgnoreCase(str_2, strs_2));
// 测试3:前str为null
String str_3 = null;
String[] strs_3 = new String[] {"ABCD", "Rql", "hello!"};
Assert.assertFalse(StringUtils.inStringIgnoreCase(str_3, strs_3));
// 测试4:后strs为null
String str_4 = "Rql!";
String[] strs_4 = null;
Assert.assertFalse(StringUtils.inStringIgnoreCase(str_4, strs_4));
// 测试5:后strs至少一个元素为null
String str_5 = null;
String[] strs_5 = new String[] {"ABCD", "Rql", null};
Assert.assertFalse(StringUtils.inStringIgnoreCase(str_5, strs_5));
}
(3)测试uncamelCase()
// 测试uncamelCase():驼峰命名法工具 uncamelCase("helloWorld") = "hello_world"
@Test
public void testUncamel() {
StringUtils StringUtils = new StringUtils();
// 测试1:首字母均大写的驼峰式字符串
String input_1 = "ThisIsRql";
String expected_1 = "this_is_rql";
String actual_1 = StringUtils.uncamelCase(input_1);
Assert.assertEquals(expected_1, actual_1);
// 测试2:连续大写字母
String input_2 = "THISIsRQL";
String expected_2 = "t_his_is_rql";
String actual_2 = StringUtils.uncamelCase(input_2);
System.out.print(actual_2);
Assert.assertEquals(expected_2, actual_2);
// 测试3:不进行驼峰命名
String input_3 = "this_is_rql";
String expected_3 = "this_is_rql";
String actual_3 = StringUtils.uncamelCase(input_3);
Assert.assertEquals(expected_3, actual_3);
// 测试4:null
String inputnull = null;
String expectednull = null;
String actualnull = StringUtils.uncamelCase(inputnull);
Assert.assertEquals(expectednull, actualnull);
// 测试5:空字符串
String input_5 = "";
String expected_5 = "";
String actual_5 = StringUtils.uncamelCase(input_5);
Assert.assertEquals(expected_5, actual_5);
// 测试6:空格
String input_6 = " ";
String expected_6 = " ";
String actual_6 = StringUtils.uncamelCase(input_6);
Assert.assertEquals(expected_6, actual_6);
}
(4)测试getMonthHasDays()
@Test
public void testGetMonthHasDays() throws ParseException {
DateUtils DateUtils = new DateUtils();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM");
// 测试1:30天的月份
Date date1 = sdf.parse("2023-06");
Assert.assertEquals(30, DateUtils.getMonthHasDays(date1));
// 测试2:31天的月份
Date date2 = sdf.parse("2023-07");
Assert.assertEquals(31, DateUtils.getMonthHasDays(date2));
// 测试3:2月份的平年年份
Date date3 = sdf.parse("2023-02");
Assert.assertEquals(28, DateUtils.getMonthHasDays(date3));
// 测试4:2月份的闰年年份
Date date4 = sdf.parse("2024-02");
Assert.assertEquals(29, DateUtils.getMonthHasDays(date4));
// // 测试5:年为null
// Date date5 = sdf.parse("null-01");
// Assert.assertNull(DateUtils.getMonthHasDays(date5));
//
// // 测试6:年为非数串
// Date date6 = sdf.parse("rql-01");
// Assert.assertNull(DateUtils.getMonthHasDays(date6));
// 测试7:月份超出范围(期望输出异常)
Date date7 = sdf.parse("2023-00");
Assert.assertEquals(31, DateUtils.getMonthHasDays(date7));
// 测试8:月份超出范围(期望输出异常)
Date date8 = sdf.parse("2023-13");
Assert.assertEquals(31, DateUtils.getMonthHasDays(date8));
// // 测试9:年为null
// Date date9 = sdf.parse("2023-null");
// Assert.assertNull(DateUtils.getMonthHasDays(date9));
//
// // 测试10:年为非数串
// Date date10 = sdf.parse("2023-rql");
// Assert.assertNull(DateUtils.getMonthHasDays(date10));
}
(5)测试getMonthHasDays()
@Test
public void testParseDateBetweenString() {
DateUtils DateUtils = new DateUtils();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
// 测试1
String dateString1 = "2024-02-29 ~ 2024-11-30";
Date[] result1 = DateUtils.parseDateBetweenString(dateString1);
Assert.assertEquals("2024-02-29", sdf.format(result1[0]));
Assert.assertEquals("2024-11-30", sdf.format(result1[1]));
// 测试2:日期字符串中包含空格
String dateString2 = "2023- 07- 31 ~ 2025- 02- 28";
Date[] result2 = DateUtils.parseDateBetweenString(dateString2);
Assert.assertEquals("2023-07-31", sdf.format(result2[0]));
Assert.assertEquals("2025-02-28", sdf.format(result2[1]));
// 测试3
String dateString3 = "2023-04-30 ~ 2023-05-31";
Date[] result3 = DateUtils.parseDateBetweenString(dateString3);
Assert.assertEquals("2023-04-30", sdf.format(result3[0]));
Assert.assertEquals("2023-05-31", sdf.format(result3[1]));
// 测试4
String dateString4 = "2023-02-28 ~ 2024-02-29";
Date[] result4 = DateUtils.parseDateBetweenString(dateString4);
Assert.assertEquals("2023-02-28", sdf.format(result4[0]));
Assert.assertEquals("2024-02-29", sdf.format(result4[1]));
// 测试5
String dateString5 = "null-01-01 ~ 2024-02-29";
Date[] result5 = DateUtils.parseDateBetweenString(dateString5);
Assert.assertNull(result5[0]);
Assert.assertEquals("2024-02-29", sdf.format(result5[1]));
// 测试6
String dateString6 = "rql-01-01 ~ 2024-02-29";
Date[] result6 = DateUtils.parseDateBetweenString(dateString6);
Assert.assertNull(result6[0]);
Assert.assertEquals("2024-02-29", sdf.format(result6[1]));
// 测试7(期望抛出异常)
String dateString7 = "2023-00-01 ~ 2024-02-29";
Date[] result7 = DateUtils.parseDateBetweenString(dateString7);
// System.out.println(sdf.format(result7[0]));
// System.out.println(sdf.format(result7[1]));
Assert.assertEquals("2022-12-01", sdf.format(result7[0]));
Assert.assertEquals("2024-02-29", sdf.format(result7[1]));
// 测试8(期望抛出异常)
String dateString8 = "2023-13-01 ~ 2024-02-29";
Date[] result8 = DateUtils.parseDateBetweenString(dateString8);
// System.out.println(sdf.format(result8[0]));
// System.out.println(sdf.format(result8[1]));
Assert.assertEquals("2024-01-01", sdf.format(result8[0]));
Assert.assertEquals("2024-02-29", sdf.format(result8[1]));
// 测试9
String dateString9 = "2023-null-01 ~ 2024-02-29";
Date[] result9 = DateUtils.parseDateBetweenString(dateString9);
Assert.assertNull(result9[0]);
Assert.assertEquals("2024-02-29", sdf.format(result9[1]));
// 测试10
String dateString10 = "2023-rql-01 ~ 2024-02-29";
Date[] result10 = DateUtils.parseDateBetweenString(dateString10);
Assert.assertNull(result10[0]);
Assert.assertEquals("2024-02-29", sdf.format(result10[1]));
// 测试11(期望抛出异常)
String dateString11 = "2023-06-00 ~ 2024-02-29";
Date[] result11 = DateUtils.parseDateBetweenString(dateString11);
Assert.assertEquals("2023-05-31", sdf.format(result11[0]));
Assert.assertEquals("2024-02-29", sdf.format(result11[1]));
// 测试12(期望抛出异常)
String dateString12 = "2023-02-29 ~ 2024-02-29";
Date[] result12 = DateUtils.parseDateBetweenString(dateString12);
Assert.assertEquals("2023-03-01", sdf.format(result12[0]));
Assert.assertEquals("2024-02-29", sdf.format(result12[1]));
// 测试13(期望抛出异常)
String dateString13 = "2024-02-30 ~ 2024-02-29";
Date[] result13 = DateUtils.parseDateBetweenString(dateString13);
Assert.assertEquals("2024-03-01", sdf.format(result13[0]));
Assert.assertEquals("2024-02-29", sdf.format(result13[1]));
// 测试14(期望抛出异常)
String dateString14 = "2023-06-31 ~ 2024-02-29";
Date[] result14 = DateUtils.parseDateBetweenString(dateString14);
Assert.assertEquals("2023-07-01", sdf.format(result14[0]));
Assert.assertEquals("2024-02-29", sdf.format(result14[1]));
// 测试15(期望抛出异常)
String dateString15 = "2023-07-32 ~ 2024-02-29";
Date[] result15 = DateUtils.parseDateBetweenString(dateString15);
Assert.assertEquals("2023-08-01", sdf.format(result15[0]));
Assert.assertEquals("2024-02-29", sdf.format(result15[1]));
// 测试16
String dateString16 = "2023-06-null ~ 2024-02-29";
Date[] result16 = DateUtils.parseDateBetweenString(dateString16);
Assert.assertNull(result16[0]);
Assert.assertEquals("2024-02-29", sdf.format(result16[1]));
// 测试17
String dateString17 = "2023-06-rql ~ 2024-02-29";
Date[] result17 = DateUtils.parseDateBetweenString(dateString17);
Assert.assertNull(result17[0]);
Assert.assertEquals("2024-02-29", sdf.format(result17[1]));
// 测试18
String dateString18 = "2023-06-13 至 2024-02-29";
Date[] result18 = DateUtils.parseDateBetweenString(dateString18);
Assert.assertNull(result18[0]);
Assert.assertNull(result18[1]);
// 测试19(期望抛出异常)
String dateString19 = "2023-06-13 ~ 2022-02-28";
Date[] result19 = DateUtils.parseDateBetweenString(dateString19);
Assert.assertEquals("2023-06-13", sdf.format(result19[0]));
Assert.assertEquals("2022-02-28", sdf.format(result19[1]));
// 测试20
String dateString20 = "2023-06-13 ~ null-02-28";
Date[] result20 = DateUtils.parseDateBetweenString(dateString20);
Assert.assertEquals("2023-06-13", sdf.format(result20[0]));
Assert.assertNull(result20[1]);
// 测试21
String dateString21 = "2023-06-13 ~ rql-02-28";
Date[] result21 = DateUtils.parseDateBetweenString(dateString21);
Assert.assertEquals("2023-06-13", sdf.format(result21[0]));
Assert.assertNull(result21[1]);
// 测试22(期望抛出异常)
String dateString22 = "2023-06-13 ~ 2024-00-01";
Date[] result22 = DateUtils.parseDateBetweenString(dateString22);
Assert.assertEquals("2023-06-13", sdf.format(result22[0]));
Assert.assertEquals("2023-12-01", sdf.format(result22[1]));
// 测试23(期望抛出异常)
String dateString23 = "2023-06-13 ~ 2024-13-01";
Date[] result23 = DateUtils.parseDateBetweenString(dateString23);
Assert.assertEquals("2023-06-13", sdf.format(result23[0]));
Assert.assertEquals("2025-01-01", sdf.format(result23[1]));
// 测试24
String dateString24 = "2023-06-13 ~ 2024-null-01";
Date[] result24 = DateUtils.parseDateBetweenString(dateString24);
Assert.assertEquals("2023-06-13", sdf.format(result23[0]));
Assert.assertNull(result24[1]);
// 测试25
String dateString25 = "2023-06-13 ~ 2024-rql-01";
Date[] result25 = DateUtils.parseDateBetweenString(dateString25);
Assert.assertEquals("2023-06-13", sdf.format(result25[0]));
Assert.assertNull(result25[1]);
// 测试26(期望抛出异常)
String dateString26 = "2023-06-13 ~ 2023-05-01";
Date[] result26 = DateUtils.parseDateBetweenString(dateString26);
Assert.assertEquals("2023-06-13", sdf.format(result26[0]));
Assert.assertEquals("2023-05-01", sdf.format(result26[1]));
// 测试27(期望抛出异常)
String dateString27 = "2023-06-13 ~ 2023-07-00";
Date[] result27 = DateUtils.parseDateBetweenString(dateString27);
Assert.assertEquals("2023-06-13", sdf.format(result27[0]));
Assert.assertEquals("2023-06-30", sdf.format(result27[1]));
// 测试28(期望抛出异常)
String dateString28 = "2023-06-13 ~ 2025-02-29";
Date[] result28 = DateUtils.parseDateBetweenString(dateString28);
Assert.assertEquals("2023-06-13", sdf.format(result28[0]));
Assert.assertEquals("2025-03-01", sdf.format(result28[1]));
// 测试29(期望抛出异常)
String dateString29 = "2023-06-13 ~ 2024-02-30";
Date[] result29 = DateUtils.parseDateBetweenString(dateString29);
Assert.assertEquals("2023-06-13", sdf.format(result29[0]));
Assert.assertEquals("2024-03-01", sdf.format(result29[1]));
// 测试30(期望抛出异常)
String dateString30 = "2023-06-13 ~ 2023-06-31";
Date[] result30 = DateUtils.parseDateBetweenString(dateString30);
Assert.assertEquals("2023-06-13", sdf.format(result30[0]));
Assert.assertEquals("2023-07-01", sdf.format(result30[1]));
// 测试31(期望抛出异常)
String dateString31 = "2023-06-13 ~ 2023-07-32";
Date[] result31 = DateUtils.parseDateBetweenString(dateString31);
Assert.assertEquals("2023-06-13", sdf.format(result31[0]));
Assert.assertEquals("2023-08-01", sdf.format(result31[1]));
// 测试32
String dateString32 = "2023-06-13 ~ 2023-07-null";
Date[] result32 = DateUtils.parseDateBetweenString(dateString32);
Assert.assertEquals("2023-06-13", sdf.format(result32[0]));
Assert.assertNull(result32[1]);
// 测试33
String dateString33 = "2023-06-13 ~ 2023-07-rql";
Date[] result33 = DateUtils.parseDateBetweenString(dateString33);
Assert.assertEquals("2023-06-13", sdf.format(result33[0]));
Assert.assertNull(result33[1]);
// 测试34(期望抛出异常)
String dateString34 = "2023-06-13 ~ 2023-06-10";
Date[] result34 = DateUtils.parseDateBetweenString(dateString34);
Assert.assertEquals("2023-06-13", sdf.format(result34[0]));
Assert.assertEquals("2023-06-10", sdf.format(result34[1]));
}
4.2.2 测试结果
4.3 缺陷分析
4.3.1 htmlAbbr()方法
(1)分析1:缺少对非数串输入的判断
针对设计的8个测试用例,经测试发现用例8为错误用例。
按实际情况来看,编写的用例中length为字符串“rql”,应抛出异常提示用户输入int类型变量。说明该方法在length输入时,程序没有检测到string类型的情况,而只考虑了用户只可能输入int类型的数字。由此,推断源程序缺少对非数串输入的判断。
程序修改:增加对于无效字符串的判断。
4.3.2 getMonthHasDays()方法
针对设计的10个测试用例,经测试发现用例5、6、9、10为错误用例,用例7、8的预期输出结果与实际输出结果不符。
(1)分析1:缺少对非date类型字符串输入的判断
按实际情况来看,用例5、6、9、10中的输入字符串中的年或月为非数串或空形式,应抛出异常。说明该方法在输入时,程序没有检测到不符合正常date类型字符串的情况,而只考虑了用户只可能输入符合date类型的字符串。由此,推断源程序缺少对不满足date类型字符串输入的判断。
(2)分析2:缺少对月份合理性的判断
用例7、8的预期结果和实际结果对比如下表所示。
表4-16 getMonthHasDays()方法用例预期与实际不同的对比
用例 | 输入参数s | 预期结果 | 实际结果 |
7 | 2023-00 | 抛出异常 | 31 |
8 | 2023-13 | 抛出异常 | 31 |
按照实际情况,上述两个用例的月份均超出了正常月份范围,应当抛出异常。然而,该方法将它们认为正常的输入,并输出的结果为31。由此,推断源代码中缺少对月份范围的判断。
这是由于方法中直接调用了date类解析输入,但date类中对于不符合的月份输入会默认修改(直接满12加到后一年的月份,小于1的往前一年中推移),导致最终实际的测试结果与预期不符。
程序修改:可以在原方法中增加对于月份范围的判断,可以在计算月份前先判断当前月份是否在1-12之间,若符合则进行后续计算;若不符合则抛出异常。也可以另写date类,增加对月份范围的判断。
4.3.3 ParseDateBetweenString()方法
针对设计的34个测试用例,经测试发现用例7、8、11、12、13、14、15、19、22、23、26、27、28、29、30、31、34的预期输出结果与实际输出结果不符。
(1)分析1:缺少对日期合理性的判断
表4-17 getMonthHasDays()方法用例预期与实际不同的对比-1
用例 | 输入参数 | 期望输出 | 实际输出 | ||
date1 | connect | date2 | |||
7 | 2023-00-01 | ~ | 2024-02-29 | 抛出异常 | ["2022-12-01", "2024-02-29"] |
8 | 2023-13-01 | ~ | 2024-02-29 | 抛出异常 | ["2024-01-01", "2024-02-29"] |
11 | 2023-06-00 | ~ | 2024-02-29 | 抛出异常 | ["2023-05-31", "2024-02-29"] |
12 | 2023-02-29 | ~ | 2024-02-29 | 抛出异常 | ["2023-03-01", "2024-02-29"] |
13 | 2024-02-30 | ~ | 2024-02-29 | 抛出异常 | ["2024-03-01", "2024-02-29"] |
14 | 2023-06-31 | ~ | 2024-02-29 | 抛出异常 | ["2023-07-01", "2024-02-29"] |
15 | 2023-07-32 | ~ | 2024-02-29 | 抛出异常 | ["2023-08-01", "2024-02-29"] |
22 | 2023-06-13 | ~ | 2024-00-01 | 抛出异常 | ["2023-06-13", "2023-12-01"] |
23 | 2023-06-13 | ~ | 2024-13-01 | 抛出异常 | ["2023-06-13", "2025-01-01"] |
27 | 2023-06-13 | ~ | 2023-07-00 | 抛出异常 | ["2023-06-13", "2023-06-30"] |
28 | 2023-06-13 | ~ | 2025-02-29 | 抛出异常 | ["2023-06-13", "2025-03-01"] |
29 | 2023-06-13 | ~ | 2024-02-30 | 抛出异常 | ["2023-06-13", "2024-03-01"] |
30 | 2023-06-13 | ~ | 2023-06-31 | 抛出异常 | ["2023-06-13", "2023-07-01"] |
31 | 2023-06-13 | ~ | 2023-07-32 | 抛出异常 | ["2023-06-13", "2023-08-01"] |
上述测试用例中,用例7和22的月份小于了正常的月份范围,用例8和23的月份大于了正常的月份范围,用例11和27的日小于了正常的日期范围,用例12和28不满足平年2月日期范围,用例13和29不满足闰年2月日期范围,用例14和30不满足小月日期范围,用例15和31不满足大月日期范围,按实际情况来看,应当抛出异常。
但该方法将它们认为正常的输入,并输出相应的数组结果。说明在字符串输入时,程序没有检测日期合理性,而只考虑了用户只可能输入合法的日期。由此,推断源程序缺少对日期合理性的判断。
这是由于方法中直接调用了date类解析输入,但date类中对于不符合的日期输入会默认修改(直接满实际日期的往后面推移,小于实际日期的往前推移),导致最终实际的测试结果与预期不符。
程序修改:可以在原方法中增加对于日期合理性的判断。在进行字符串到数组的转换前,先将输入后利用split()方法得到的begin和end两个日期进行判断,要保证它们的月份在1-12之间,日符合相应的要求(大月日在31内,小月日在30内,闰年2月日在29内,平年2月日在28内)。也可以另写date类,增加对日期合理性的判断。
(2)分析2:缺少对两个日期前后性的判断
表4-18 getMonthHasDays()方法用例预期与实际不同的对比-2
用例 | 输入参数 | 期望输出 | 实际输出 | ||
date1 | connect | date2 | |||
19 | 2023-06-13 | ~ | 2022-02-28 | 抛出异常 | ["2023-06-13", "2022-02-28"] |
26 | 2023-06-13 | ~ | 2023-05-01 | 抛出异常 | ["2023-06-13", "2023-05-01"] |
34 | 2023-06-13 | ~ | 2023-06-10 | 抛出异常 | ["2023-06-13", "2023-06-10"] |
上述测试用例中,前一个日期均小于后一个日期。按实际情况来看,应抛出异常。但该方法将它们认为正常的输入,并输出相应的数组结果。说明在字符串输入时,程序没有检测日期前后性,而只考虑了用户只可能输入从前往后的日期。由此,推断源程序缺少对日期前后性的判断。
程序修改:增加对于日期前后性的判断。在进行字符串到数组的转换前,先将输入后利用split()方法得到的begin和end两个日期进行前后判断,要保证前一个日期在后一个日期之前,即满足:(year1 < year2)或者(year1 = year2且month1 < month2)或者(year1 = year2且month1=month2且day1<=day2)。
4.3.4 InString()方法
设计的5个用例均顺利通过测试,未发现缺陷。
4.3.5 uncamelCase()方法
设计的6个用例均顺利通过测试,未发现缺陷。