【个人项目】跑者天地—测试报告
1. 项目概述
背景:复习测试用例这一块知识,我选择以这个项目为例进行练习,系统梳理测试用例相关知识。
概述:本项目是一个基于Web的运动社交平台,用户可以在线分享跑步心得,结识兴趣相同的运动伙伴,共同追求健康生活。
相应技术栈:SpringBoot、HTML、CSS、JavaScript、Ajax、Json、MySQL,Token
2. 测试目的和工具
2.1 测试目的
本测试报告为跑者天地项目的测试报告,目的在于总结测试阶段以及分析测试结果,描述系统是否符合需求。预期参考人员包括用户、测试人员、开发人员、项目管理者、其他质量管理人员。本次测试的目的在于确保系统功能测试和自动化测试正常无误,满足系统需要
2.2 测试工具
本次测试使用的工具有Fiddler,Postman,开发者工具等,其中Fiddler用于抓包和模拟弱网环境,详细对HTTP请求进行分析,Postman用户模拟客户端向服务器发送请求,开发者工具用于白盒测试,从代码层出发,观察数据走向
3. 测试主要任务
针对项目UI界面进行全面测试,分别对登录页面,注册页面,文章列表页面,文章详情页面,文章发布页面,个人中心页面,退出登录页面进行测试,确保各页面功能逻辑正确,界面布局合理,交互流畅。
3.1 设计测试用例
3.1.1 Xmind格式
1)登录页面
2)注册页面
3)文章列表页
4)文章详情页
5)文章发布页
6)个人中心页
7)退出登录页
3.1.2 Excel格式
1)登录页面
用例编号 | 优先级 | 测试标题 | 测试环境 | 测试类型 | 前置条件 | 测试步骤 | 预期结果 |
---|---|---|---|---|---|---|---|
1 | P0 | 正常登录功能验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 存在有效账号 | 1. 进入登录页面 2. 输入正确的账号,密码,验证码 3. 点击登录 | 跳转到文章列表页 |
2 | P0 | 空输入异常处理 | Win11+Chrome 139.0.7258.67 | 功能测试 | 登录页加载完成 | 1. 进入登录页面 2. 点击登录 | 提示“账号不能为空” |
3 | P1 | 验证码刷新有效性验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 登录页加载完成 | 1. 进入登录页面 2. 点击验证码图片 3. 输入正确的账号,密码,验证码 4. 点击登录 | 跳转到文章列表页 |
4 | P0 | SQL注入攻击防护 | Win11+Chrome 139.0.7258.67 | 功能测试 | 登录页加载完成 | 1. 进入登录页面 2. 输入正确的账号(' OR 1=1--) 3. 输入密码,验证码 4. 点击登录 | 登录失败 |
5 | P2 | 界面内容溢出检测 | Win11+Chrome 139.0.7258.67 | 界面测试 | 登录页加载完成 | 1. 输入30字符长账号 2. 输入50字符密码 3. 查看页面显示 | 无内容截断 |
2)注册页面
用例编号 | 优先级 | 测试标题 | 测试环境 | 测试类型 | 前置条件 | 测试步骤 | 预期结果 |
---|---|---|---|---|---|---|---|
1 | P0 | 正常注册功能验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 进入注册页面 | 1. 输入合规用户名 2. 输入11位手机号 3. 设置密码=确认密码 4. 勾选用户协议 5. 点击注册按钮 | 注册成功 |
2 | P0 | 手机号格式异常验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 进入注册页面 | 1. 输入用户名 2. 输入10位手机号 3. 设置有效密码 4. 勾选协议 | 提示"手机号格式错误" |
3 | P0 | 密码一致性验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 进入注册页面 | 1. 输入用户名 2. 输入有效手机号 3. 密码与确认密码不同 4. 勾选协议 | 实时提示"两次密码不一致" |
4 | P1 | 协议未勾选注册拦截 | Win11+Chrome 139.0.7258.67 | 功能测试 | 进入注册页面 | 1. 输入有效注册信息 2. 不勾选用户协议 3. 点击注册按钮 | 提示"请阅读并同意用户协议" |
5 | P1 | SQL注入攻击防御验证 | Win11+Chrome 139.0.7258.67 | 安全性测试 | 进入注册页面 | 1. 用户名输入:admin'-- 2. 手机号输入:'; DROP TABLE users;-- 3. 设置有效密码 4. 勾选协议 | 注册失败,数据库无异常 |
6 | P1 | 特殊字符输入稳定性验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 进入注册页面 | 1. 用户名输入:!@#$%^&*() 2. 手机号输入有效号码 3. 设置有效密码 4. 勾选协议 | 系统正常处理字符,无崩溃/卡死现象 |
3)文章列表页面
用例编号 | 优先级 | 测试标题 | 测试环境 | 测试类型 | 前置条件 | 测试步骤 | 预期结果 |
---|---|---|---|---|---|---|---|
1 | P0 | 发帖按钮跳转功能验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 已登录状态 | 1. 进入文章列表页 2. 点击"立即发帖"按钮 | 成功跳转到文章编写页面 |
2 | P0 | 浏览排序功能验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 列表有≥3篇文章 | 1. 选择"最多浏览"排序模式 2. 记录前三篇文章浏览量 3. 验证排序顺序 | 文章按浏览量降序排列(浏览量高的在前) |
3 | P0 | 文章跳转功能验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 列表有≥1篇文章 | 1. 点击任意文章标题 2. 检查页面跳转 | 成功跳转到对应文章详情页 |
4 | P1 | 主页按钮刷新功能 | Win11+Chrome 139.0.7258.67 | 功能测试 | 列表有文章 | 1. 修改列表内容 2. 点击顶部"主页"按钮 | 页面刷新并显示最新内容 |
5 | P1 | 未登录URL访问验证 | Win11+Chrome 139.0.7258.67 | 安全性测试 | 未登录状态 | 1. 直接输入文章列表URL访问 | 跳转至登录页 |
6 | P1 | 越权访问验证 | Win11+Chrome 139.0.7258.67 | 安全性测试 | 用户A已登录 | 1. 修改URL中的用户ID为其他用户 2. 尝试访问 | 显示无权限,并返回登录页面 |
4)文章详情页面
用例编号 | 优先级 | 测试标题 | 测试环境 | 测试类型 | 前置条件 | 测试步骤 | 预期结果 |
---|---|---|---|---|---|---|---|
1 | P0 | 文章内容加载验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 访问文章详情页 | 1. 打开文章详情页 | 正确显示文章标题、正文信息 |
2 | P0 | 作者编辑功能验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 当前用户=文章作者 | 1. 点击"编辑文章"按钮 | 跳转至文章编辑页面 |
3 | P0 | 浏览量增加验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 记录当前浏览量 | 1. 刷新页面 2. 重新进入详情页 | 浏览量增加1 |
4 | P0 | 主页按钮跳转验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 在文章详情页 | 1. 点击顶部"主页"按钮 | 跳转至文章列表页 |
5 | P1 | 非作者删除权限拦截 | Win11+Chrome 139.0.7258.67 | 安全性测试 | 当前用户≠文章作者 | 1. 尝试点击"删除文章"按钮 | 提示"不是作者,无法删除" |
6 | P1 | 未登录访问拦截 | Win11+Chrome 139.0.7258.67 | 安全性测试 | 未登录状态 | 1. 直接输入文章详情页URL访问 | 跳转至登录页 |
5)文章发布页面
用例编号 | 优先级 | 测试标题 | 测试环境 | 测试类型 | 前置条件 | 测试步骤 | 预期结果 |
---|---|---|---|---|---|---|---|
1 | P0 | 主页按钮跳转验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 在编写页面 | 1. 点击顶部"主页"按钮 | 跳转至文章列表页 |
2 | P0 | 发布功能验证-标题缺失 | Win11+Chrome 139.0.7258.67 | 功能测试 | 已登录状态 | 1. 输入文章内容 2. 留空标题 3. 点击发布 | 提示"标题不能为空",阻止发布 |
3 | P0 | 发布功能验证-内容缺失 | Win11+Chrome 139.0.7258.67 | 功能测试 | 已登录状态 | 1. 输入文章标题 2. 留空内容 3. 点击发布 | 提示"内容不能为空",阻止发布 |
4 | P0 | 发布成功跳转验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 输入完整内容 | 1. 输入标题和内容 2. 点击发布 | 发布成功,跳转至文章列表页 |
5 | P1 | Markdown渲染验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 输入Markdown格式 | 1. 输入#标题 2. 输入**加粗**文本 3. 预览内容 | 正确渲染为HTML格式 |
6 | P1 | 特殊字符处理验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 准备特殊字符 | 1. 标题输入: @#$%^&*() 2. 内容输入: null字符 3. 点击发布 | 系统正常处理,无崩溃 |
6)个人中心页面
用例编号 | 优先级 | 测试标题 | 测试环境 | 测试类型 | 前置条件 | 测试步骤 | 预期结果 |
---|---|---|---|---|---|---|---|
1 | P0 | 编辑状态初始化验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 已登录状态 | 1. 点击"编辑信息"按钮 | 密码字段清空,其余信息可编辑 |
2 | P0 | 必填项缺失验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 进入编辑状态 | 1. 清空账号字段 2. 点击"保存修改" | 提示"账号不能为空",阻止保存 |
3 | P0 | 密码修改有效性验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 修改密码 | 1. 输入新密码 2. 保存修改 3. 注销后使用新密码登录 | 新密码登录成功 |
4 | P0 | 原始密码失效验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 已修改密码 | 1. 使用旧密码尝试登录 | 提示"密码错误",登录失败 |
5 | P1 | 取消修改功能验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 进入编辑状态 | 1. 修改部分信息 2. 点击"取消"按钮 | 信息恢复修改前状态 |
6 | P1 | 未授权访问拦截 | Win11+Chrome 139.0.7258.67 | 安全性测试 | 未登录状态 | 1. 直接输入个人中心URL访问 | 跳转至登录页 |
7 | P1 | 密码显示安全验证 | Win11+Chrome 139.0.7258.67 | 安全性测试 | 查看密码字段 | 1. 检查密码输入框 | 密码显示为掩码(*) |
8 | P1 | SQL注入防御验证 | Win11+Chrome 139.0.7258.67 | 安全性测试 | 编辑状态 | 1. 账号输入:admin'-- 2. 尝试保存 | 拦截特殊字符,保存失败 |
9 | P2 | 手机号格式校验 | Win11+Chrome 139.0.7258.67 | 功能测试 | 修改手机号 | 1. 输入错误格式号码(如10位) | 实时提示"手机号格式错误" |
7)退出登录页面
用例编号 | 优先级 | 测试标题 | 测试环境 | 测试类型 | 前置条件 | 测试步骤 | 预期结果 |
---|---|---|---|---|---|---|---|
1 | P0 | 主页按钮跳转验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 在注销页面 | 1. 点击顶部"主页"按钮 | 跳转至文章列表页 |
2 | P0 | 注销流程验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 已登录状态 | 1. 点击"注销"按钮 2. 点击"确认退出" 3. 点击成功提示的"确定" | 成功退出,跳转至登录页 |
3 | P0 | 取消注销功能验证 | Win11+Chrome 139.0.7258.67 | 功能测试 | 打开注销确认框 | 1. 点击"取消"按钮 | 返回原页面,保持登录状态 |
4 | P1 | 令牌删除验证 | Win11+Chrome 139.0.7258.67 | 安全性测试 | 已执行注销 | 1. 检查localStorage/user_token字段是否存在 | 身份令牌被清除 |
5 | P1 | 未登录访问拦截 | Win11+Chrome 139.0.7258.67 | 安全性测试 | 未登录状态 | 1. 直接输入注销页面URL访问 | 跳转至登录页 |
6 | P1 | 弱网注销处理 | Win11+Chrome 139.0.7258.67 | 网络测试 | 已登录状态 | 1. 弱网环境下执行注销操作 | 2秒内显示加载图标,超时提示 |
3.2 执行测试用例
考虑篇幅过长,这里只分享部分测试用例的执行
3.2.1 注册测试
正常注册
- 打开谷歌浏览器
- 输入网址 :http://127.0.0.1:8080/register.html
- 输入用户名,手机号码,密码,确认密码
- 勾选协议
- 点击注册按钮
结果:提示注册成功
异常注册
结果:将错误的原因直接显示出来
- 打开谷歌浏览器
- 输入网址 :http://127.0.0.1:8080/register.html
- 输入用户名,手机号码,密码,错误的确认密码
- 未勾选协议
- 点击注册
3.2.2 登录测试
正常登录
- 打开谷歌浏览器
- 输入网址 :http://127.0.0.1:8080/login.html
- 输入用户名,密码,验证码
- 点击登录
结果:跳转到文章列表页
异常登录
- 打开谷歌浏览器
- 输入网址 :http://127.0.0.1:8080/login.html
- 输入用户名,密码,验证码
- 点击登录
结果:登录失败并提示错误原因
3.2.3 文章发布测试
前置条件:已登录(满足)
发布成功
- 打开谷歌浏览器
- 输入网址 :http://127.0.0.1:8080/write.html
- 输入文章标题,内容,文章类别
- 点击发布
结果:跳转到文章列表页,可以观察到刚刚发布的文章
异常发布
- 打开谷歌浏览器
- 输入网址 :http://127.0.0.1:8080/write.html
- 未输入文章标题,输入内容,文章类别
- 点击发布
结果:发布失败并提示原因
3.2.4 修改文章
前置条件:文章的作者和登录用户一致(满足)
修改成功
- 打开谷歌浏览器
- 输入网址 :http://127.0.0.1:8080/update.html?articleId=59
- 输入新的标题
- 点击发布
结果:提示修改成功
修改失败
- 打开谷歌浏览器
- 输入网址 :http://127.0.0.1:8080/update.html?articleId=59
- 删除标题内容
- 点击发布
结构:提示修改失败
3.2.5 删除文章
删除成功
- 打开谷歌浏览器
- 输入网址 :http://127.0.0.1:8080/DetailPage.html?articleId=59
- 前置条件:文章的作者和登录用户一致
- 点击删除文章按钮
结果:提示删除成功
删除失败
- 打开谷歌浏览器
- 输入网址 :http://127.0.0.1:8080/DetailPage.html?articleId=57
- 前置条件:文章的作者和登录用户不一致
- 点击删除文章按钮
结果:提示无法删除
3.2.6 修改个人信息测试
前置条件:已登录(满足)
修改成功
- 打开谷歌浏览器
- 输入网址 :http://127.0.0.1:8080/me.html
- 点击编辑信息按钮
- 输入新的密码123123
- 点击保存修改
结果:提示修改成功
3.3 自动化测试
3.3.1 自动化测试环境
- 操作系统:Windows11
- 浏览器:谷歌浏览器
- 浏览器版本:139.0.7258.128(正式版本) (64 位)
- 数据库:MySQL 8.0
- 测试工具:selenium,JUnit 5.9.1
3.3.2 自动化测试用例
3.3.3 自动化代码编写
1)导入依赖
<dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>4.0.0</version></dependency><dependency><groupId>io.github.bonigarcia</groupId><artifactId>webdrivermanager</artifactId><version>5.5.3</version></dependency>
2)创建测试包和类
为了避免自动化混乱,从页面为模块进行分类,每个页面创建单独的类进行管理,类中提供多个方法,对页面中的属性和功能进行测试
3)创建公共类
Utils 公共类中包含一些公共方法,比如创建驱动,截屏操作,关闭驱动操作
package org.example.runningforum.common;import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.Duration;public class Utils {public static WebDriver driver;public Utils(String url){driver = createDriver();driver.get(url);}/** 创建谷歌浏览器驱动* */public static WebDriver createDriver(){if(driver == null){//初始化驱动管理
// WebDriverManager.chromedriver().setup();//设置浏览器属性ChromeOptions options = new ChromeOptions();options.addArguments("--remote-allow-origins=*");//创建驱动对象driver = new ChromeDriver();//添加隐式等待driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(2));}return driver;}public static void getScreenShot(String className,String methodName) throws IOException {SimpleDateFormat ymd = new SimpleDateFormat("yyyy-MM-dd");SimpleDateFormat hms = new SimpleDateFormat("HH-mm-ss-SS");String dirPath = ymd.format(System.currentTimeMillis());String fileName = className+"-"+methodName+"-"+hms.format(System.currentTimeMillis());String filePath = "./running-forum/src/test/java/org/example/runningforum/image/"+dirPath+"/"+fileName+".png";File srcFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);FileUtils.copyFile(srcFile,new File(filePath));}public void closeDriver(){if(driver!=null){driver.quit();}}
}
4)注册界面
自动化主要有三个核心方法
- 验证界面加载是否正确
- 验证注册成功
- 验证注册失败
package org.example.runningforum.tests;import org.example.runningforum.common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;import java.io.IOException;public class Register extends Utils {public static String url = "http://127.0.0.1:8080/register.html";public Register() {super(url);}/** 检查界面是否加载完毕* */public void registerPageRight(){driver.findElement(By.cssSelector("#username"));driver.findElement(By.cssSelector("#mobile"));driver.findElement(By.cssSelector("#password"));driver.findElement(By.cssSelector("#confirmPassword"));driver.findElement(By.cssSelector("#terms"));}/** 检查页面注册功能 —— 注册成功* */public void registerSuccess() throws IOException, InterruptedException {String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();driver.navigate().refresh();driver.findElement(By.cssSelector("#username")).sendKeys("user1");driver.findElement(By.cssSelector("#mobile")).sendKeys("19635264587");driver.findElement(By.cssSelector("#password")).sendKeys("123456");driver.findElement(By.cssSelector("#confirmPassword")).sendKeys("123456");//勾选用户协议driver.findElement(By.cssSelector("#terms")).click();getScreenShot(getClass().getName(),methodName);driver.findElement(By.cssSelector("#registerButton")).click();//必须强制等待Thread.sleep(500);Alert alert = driver.switchTo().alert();String alertText = alert.getText();assert alertText.equals("注册成功");//在警告的时候,不能进行其他操作alert.accept();getScreenShot(getClass().getName(),methodName);}/** 检查页面注册功能 —— 注册失败* */public void registerFail() throws IOException, InterruptedException {String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();driver.navigate().refresh();driver.findElement(By.cssSelector("#username")).sendKeys("user1");driver.findElement(By.cssSelector("#mobile")).sendKeys("19635264587");driver.findElement(By.cssSelector("#password")).sendKeys("123456");driver.findElement(By.cssSelector("#confirmPassword")).sendKeys("123456");//勾选用户协议driver.findElement(By.cssSelector("#terms")).click();getScreenShot(getClass().getName(),methodName);driver.findElement(By.cssSelector("#registerButton")).click();Thread.sleep(500);Alert alert = driver.switchTo().alert();String alertText = alert.getText();assert alertText.equals("用户名被使用");alert.accept();getScreenShot(getClass().getName(),methodName);}
}
5)登录界面
自动化测试主要有三个核心方法
- 验证界面加载是否正确
- 验证登录成功
- 验证登录失败
package org.example.runningforum.tests;import org.example.runningforum.common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;import java.io.IOException;public class LoginPage extends Utils {public static String url = "http://127.0.0.1:8080/login.html";public LoginPage() {super(url);}/** 检查元素是否加载完毕* */public void loginPageRight(){driver.findElement(By.cssSelector("#username"));driver.findElement(By.cssSelector("#password"));driver.findElement(By.cssSelector("#inputCaptcha"));}/** 验证登录功能--成功登录* */public void loginSuccess() throws IOException {//获取方法名String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();//刷新,保证输入框没有元素driver.navigate().refresh();//输入信息driver.findElement(By.cssSelector("#username")).sendKeys("user1");driver.findElement(By.cssSelector("#password")).sendKeys("123456");//获取验证码String text = driver.findElement(By.cssSelector("#verificationCodeImg")).getText();driver.findElement(By.cssSelector("#inputCaptcha")).sendKeys(text);//截图getScreenShot(getClass().getName(), methodName );//点击登录driver.findElement(By.cssSelector("#submit")).click();//检验是否跳转到列表页String expect = driver.findElement(By.cssSelector("body > div.container > div.welcome-banner > h1")).getText();//使用断言判断assert expect.equals("欢迎来到跑者天地");//截图getScreenShot(getClass().getName(), methodName );}/** 验证登录功能--登录失败* */public void loginFail() throws IOException {//获取方法名String methodName = Thread.currentThread().getStackTrace()[1].getMethodName();//刷新,保证输入框没有元素driver.navigate().refresh();//输入信息driver.findElement(By.cssSelector("#username")).sendKeys("admin");driver.findElement(By.cssSelector("#password")).sendKeys("123");//获取验证码String text = driver.findElement(By.cssSelector("#verificationCodeImg")).getText();driver.findElement(By.cssSelector("#inputCaptcha")).sendKeys(text);//截图getScreenShot(getClass().getName(), methodName );Alert alert = driver.switchTo().alert();String alertText = alert.getText();assert alertText.equals("账号或密码错误");}}
6)文章列表页面
自动化测试主要测试:
- 界面加载是否正确
- 是否根据浏览量排序文章
- 是否根据文件发布时间排序文章
package org.example.runningforum.tests;import org.example.runningforum.common.Utils;
import org.openqa.selenium.By;import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;public class ListPage extends Utils {public static String url = "http://127.0.0.1:8080/index.html?currentPage=1";public ListPage() {super(url);}/** 验证界面加载* */public void listPageRight(){driver.findElement(By.cssSelector("body > div.container > div.welcome-banner > h1"));driver.findElement(By.cssSelector("#sortSelector"));driver.findElement(By.cssSelector("body > div.container > div.welcome-banner > a"));driver.findElement(By.cssSelector("#header-container > header > div > div > nav > ul > li:nth-child(1) > a"));}/** 验证列表界面功能 —— 功能正常* */public void listSuccess() throws InterruptedException {listPageRight();driver.navigate().refresh();//验证最新发布driver.findElement(By.cssSelector("#sortSelector > option:nth-child(1)")).click();String timeStr1 = driver.findElement(By.cssSelector("#card-body > div:nth-child(1) > div.post-content > div > span:nth-child(2)")).getText();String timeStr2 = driver.findElement(By.cssSelector("#card-body > div:nth-child(2) > div.post-content > div > span:nth-child(2)")).getText();//比较时间DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH-mm");// 解析为 LocalDateTime 对象LocalDateTime time1 = LocalDateTime.parse(timeStr1, formatter);LocalDateTime time2 = LocalDateTime.parse(timeStr2, formatter);int comparison = time1.compareTo(time2);assert comparison>0;driver.findElement(By.cssSelector("#sortSelector > option:nth-child(2)")).click();Thread.sleep(500);Integer count1 = Integer.valueOf(driver.findElement(By.cssSelector("#card-body > div:nth-child(1) > div.post-content > div > span:nth-child(3) ")).getText());Integer count2 = Integer.valueOf(driver.findElement(By.cssSelector("#card-body > div:nth-child(2) > div.post-content > div > span:nth-child(3) ")).getText());assert count1>count2;}}
7)文章发布页面
自动化测试主要验证:
- 发布界面是否加载正确
- 发布功能是否正常
package org.example.runningforum.tests;import org.example.runningforum.common.Utils;
import org.openqa.selenium.By;public class WritePage extends Utils {public static String url = "http://127.0.0.1:8080/write.html";public WritePage() {super(url);}/** 验证页面加载* */public void writePageRight(){driver.navigate().refresh();String title = driver.findElement(By.cssSelector("body > div.content-edit > div.create-post-header > h1")).getText();assert "创作新文章".equals(title);driver.findElement(By.cssSelector("#title"));driver.findElement(By.cssSelector("#editor > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre > span > span"));}/** 发布成功* */public void WriteSuccess(){driver.navigate().refresh();driver.findElement(By.cssSelector("#title")).sendKeys("跑步技巧");driver.findElement(By.cssSelector("#submit")).click();String text = driver.findElement(By.cssSelector("body > div.container > div.welcome-banner > h1")).getText();assert "欢迎来到跑者天地".equals(text);}}
8)修改文章页面
自动化测试主要验证:
- 界面是否加载正确
- 修改文章内容是否成功
package org.example.runningforum.tests;import org.example.runningforum.common.Utils;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;public class UpdatePage extends Utils {public static String url = "http://127.0.0.1:8080/update.html?articleId=45";public UpdatePage() {super(url);}/** 验证界面记载* */public void updatePageRight(){String text = driver.findElement(By.cssSelector("body > div.content-edit > div.create-post-header > h1")).getText();assert "修改文章".equals(text);text = driver.findElement(By.cssSelector("#submit")).getText();assert "发布文章".equals(text);}/** 验证功能 — 成功* */public void updateSuccess() throws InterruptedException {driver.findElement(By.cssSelector("#title")).clear();driver.findElement(By.cssSelector("#title")).sendKeys("新标题");WebElement element = driver.findElement(By.cssSelector("#editor > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre > span > span"));Actions actions = new Actions(driver);//先选中位置actions.doubleClick(element).perform();//全选操作actions.sendKeys(Keys.chord(Keys.COMMAND, "a")).perform();//删除内容actions.keyDown(Keys.DELETE).perform();//添加内容actions.sendKeys("运动对身体好").perform();driver.findElement(By.cssSelector("#submit")).click();Thread.sleep(500);String alertText = driver.switchTo().alert().getText();assert "修改成功".equals(alertText);driver.switchTo().alert().accept();//判断是否修改成功String newContent = driver.findElement(By.cssSelector("#card-body > div:nth-child(1) > div.post-content > p")).getText();assert "运动对身体好".equals(newContent);}
}
9)个人信息页面
自动化测试主要验证:
- 界面是否加载正确
- 个人信息修改是否成功
- 修改途中取消修改,是否成功
package org.example.runningforum.tests;import org.example.runningforum.common.Utils;
import org.openqa.selenium.By;public class MePage extends Utils {public static String url = "http://127.0.0.1:8080/me.html";public MePage() {super(url);}/** 检验界面加载* */public void mePageRight(){String text = driver.findElement(By.cssSelector("body > div.container > div > div.card-header > h1")).getText();assert "跑者信息中心".equals(text);text = driver.findElement(By.cssSelector("#edit-btn")).getText();assert "编辑信息".equals(text);}/** 检验编辑功能——编辑成功* */public void mePageSuccess(){driver.navigate().refresh();driver.findElement(By.cssSelector("#edit-btn")).click();String password = driver.findElement(By.cssSelector("#password-input")).getText();assert password == null;driver.findElement(By.cssSelector("#password-input")).sendKeys("123456");driver.findElement(By.cssSelector("#save-btn")).click();String text = driver.findElement(By.cssSelector("#notification-text")).getText();assert text.equals("用户信息更新成功!");}/** 检验功能——取消编辑* */public void mePageFail(){driver.navigate().refresh();driver.findElement(By.cssSelector("#edit-btn")).click();String password = driver.findElement(By.cssSelector("#password-input")).getText();assert password == null;driver.findElement(By.cssSelector("#password-input")).sendKeys("123456");driver.findElement(By.cssSelector("#cancel-btn")).click();String text = driver.findElement(By.cssSelector("#notification-text")).getText();assert text.equals("已取消修改");}
}
10)注销界面
自动化测试主要验证:
- 界面是否加载正确
- 注销功能是否成功
- 点击取消注销是否返回首页
package org.example.runningforum.tests;import org.checkerframework.checker.units.qual.A;
import org.example.runningforum.common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;public class LogoutPage extends Utils {public static String url = "http://127.0.0.1:8080/logout.html";public LogoutPage() {super(url);}/** 验证页面加载* */public void loginOutPageRight(){driver.navigate().refresh();String text = driver.findElement(By.cssSelector("body > div.container > div > div > div > h2")).getText();assert text.equals("您确定要退出登录吗?");text = driver.findElement(By.cssSelector("#logout-btn")).getText();assert text.equals("确认退出");}/** 验证功能——成功* */public void loginOutSuccess(){driver.navigate().refresh();driver.findElement(By.cssSelector("#logout-btn")).click();Alert alert = driver.switchTo().alert();String alertText = alert.getText();assert alertText.equals("您已成功退出登录");alert.accept();//是否退回登录页面String text = driver.findElement(By.cssSelector("body > div.container > div.login-dialog > h3")).getText();assert text.equals("用户登录");}/** 验证功能——失败* */public void loginOutFail(){driver.navigate().refresh();driver.findElement(By.cssSelector("#cancel-btn")).click();//是否退回列表页面String text = driver.findElement(By.cssSelector("body > div.container > div.welcome-banner > h1")).getText();assert text.equals("欢迎来到跑者天地");}
}
11)文章详情页面
自动化测试主要验证:
- 界面是否加载正确
- 验证删除文章和修改文章功能是否正常
package org.example.runningforum.tests;import org.example.runningforum.common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;public class DetailPage extends Utils {public static String url = "http://127.0.0.1:8080/DetailPage.html?articleId=11";public DetailPage() {super(url);}/** 验证界面加载* */public void detailPageRight(){driver.findElement(By.cssSelector("#article-header1 > h1"));driver.findElement(By.cssSelector("#header-container > header > div > div > nav > ul > li:nth-child(1) > a"));String text = driver.findElement(By.cssSelector("#article-header1 > div > div.article-stats > button.action-btn.edit-btn")).getText();assert text.equals("编辑文章");String title = driver.findElement(By.cssSelector("#article-header1 > h1")).getText();assert title != null;}/** 验证功能——操作失败* */public void detailFail() throws InterruptedException {driver.navigate().refresh();driver.findElement(By.cssSelector("#article-header1 > div > div.article-stats > button.action-btn.edit-btn")).click();Thread.sleep(500);Alert alert = driver.switchTo().alert();String alertText = alert.getText();assert alertText.equals("不是作者,无法修改");alert.accept();driver.findElement(By.cssSelector("#article-header1 > div > div.article-stats > button.action-btn.delete-btn")).click();Thread.sleep(500);alertText = alert.getText();assert alertText.equals("不是作者,无法删除");alert.accept();}/** 验证功能——操作成功* */public void detailSuccess(){driver.navigate().refresh();driver.findElement(By.cssSelector("#article-header1 > div > div.article-stats > button.action-btn.edit-btn")).click();String text = driver.findElement(By.cssSelector("body > div.content-edit > div.create-post-header > h1")).getText();assert text.equals("修改文章");driver.navigate().back();driver.findElement(By.cssSelector("#article-header1 > div > div.article-stats > button.action-btn.delete-btn")).click();Alert alert = driver.switchTo().alert();String alertText = alert.getText();assert alertText.equals("删除成功");alert.accept();}
}
3.3.4 自动化测试的执行
1)运行代码
package org.example.runningforum;import org.apache.catalina.AccessLog;
import org.example.runningforum.tests.*;
import org.springframework.boot.test.context.SpringBootTest;import java.io.IOException;@SpringBootTest
class RunningForumApplicationTests {public static void main(String[] args) throws IOException, InterruptedException {//注册Register register = new Register();register.registerPageRight();
// register.registerSuccess();;register.registerFail();//登录LoginPage loginPage = new LoginPage();loginPage.loginPageRight();loginPage.loginSuccess();
// loginPage.loginFail();//列表页面ListPage listPage = new ListPage();listPage.listPageRight();listPage.listSuccess();//详细页面DetailPage detailPage = new DetailPage();detailPage.detailPageRight();detailPage.detailFail();
// detailPage.detailSuccess();//写文章页面WritePage writePage = new WritePage();writePage.writePageRight();writePage.WriteSuccess();//修改页面UpdatePage updatePage = new UpdatePage();updatePage.updatePageRight();updatePage.updateSuccess();//个人页面MePage mePage = new MePage();mePage.mePageRight();mePage.mePageFail();mePage.mePageSuccess();//退出登录LogoutPage logoutPage = new LogoutPage();logoutPage.loginOutPageRight();
// logoutPage.loginOutFail();logoutPage.loginOutSuccess();logoutPage.closeDriver();}}
从注册用户,登录账号,验证列表界面,验证文章详情页面,验证发布文章界面,验证修改文章界面,验证其功能是否正确,验证个人信息页面,验证其是否可以修改,最后验证注销页面,退出登录,这一系列流程,模拟用户的真实使用,将核心功能覆盖,并对其进行自动化测试
2)自动化测试预览
4. 测试结果
4.1 出现的bug
由于篇幅问题,只说明其中1个
经过实际测试发现预期结果与实际结果大部分一致!需要提交修改的bug如下:
在自动化部测试的时候,系统报出元素定位失败的错误,经过一系列的检查,发现代码并不存在错误,位置定位正确,通过增加3秒强制等待,又回归正常,可以访问到元素,原因分析:页面渲染需要一定的时间,而后端中代码执行速度较快,页面无法快速显现而报错
4.2 bug提交
bug编号:01
运行环境:Windows11 + 谷歌浏览器 139.0.7258.128(正式版本) (64 位)
bug级别:P0
bug标题:自动化测试,发布文章页面出现元素定位异常
前置条件:登录状态下
bug步骤:
- 打开谷歌浏览器
- 输入网址:http://127.0.0.1:8080/write.html
- 问题元素cssSelector路径:#submit
bug的状态:已解决
自动执行预期结果:
自动化执行实际结果:
5. 总结
通过对这个项目的测试,加强了我对测试知识的掌握,从中也收获了很多测试方面的心得
- 在执行测试用例的时候,注意测试用例的执行顺序,不同的执行顺序可能会让原本正常的结果出现异常
- 保存好现场的截图,并分类好测试的时间,类名和方法,帮助测试人员更好的进行回溯,定位问题
- 使用单例模式创建驱动,省去不必要的开销
- 测试用例不是越多越好,要做到对功能的覆盖率最大化
- 合理运用等待,可以帮助定位问题和提高自动化运行效率
- 对测试的对象做好分类,避免出现自动化混乱的情况