抽奖系统 - 测试报告
项目名称:抽奖系统
测试人员:LlvZi
测试时间:2025年5月25 - 2025年6月1
一、项目概述
该项目是一个操作简便、安全可靠的抽奖系统 。主要业务是抽奖,并支持管理员管理用户、奖品和抽奖活动,以配置抽奖内容。抽奖操作由管理员进行,支持依次抽取配置好的奖品和人数,并保证每人最多中一次奖 。系统对异常情况进行了处理,并对中奖人发送短信和邮件通知。项目采用了分层分模块设计,技术栈包括 Spring Boot3、JDK 17、MySQL、MyBatis、Redis、RabbitMQ、SLF4J + logback 等。
二、测试目标
- 验证抽奖系统各项核心功能(用户管理、奖品管理、活动管理、抽奖流程)是否符合需求。
- 确保系统在正常、异常及边界条件下均能稳定运行,特别是抽奖过程中的唯一性保证。
- 验证抽奖接口的异步处理及事务一致性。
- 验证系统安全性场景(如:身份验证、隐私数据加密)。
- 检查中奖通知(短信、邮件)的及时性和准确性。
三、测试范围
模块名称 | 涵盖功能 |
---|---|
用户模块 | 注册、登录、信息管理、身份验证 (JWT) |
奖品模块 | 奖品配置、状态管理 |
活动模块 | 活动创建、配置 (奖品、人员)、状态维护 |
抽奖模块 | 抽奖操作、异步处理、异常处理、唯一性保证 |
通知模块 | 短信通知、邮件通知 |
接口测试 | 所有REST接口状态码与返回结构校验 |
数据安全与隐私保护 | 密码加密 (加盐哈希)、手机号加密 |
四、测试方法与工具
类型 | 工具或技术栈 |
---|---|
功能测试 | Postman + 手动测试 |
接口测试 | Postman |
单元测试 | 编写测试代码验证模块正确性 |
安全性测试 | 页面强制登录、抽奖异常后的奖品唯一性校验 |
日志分析 | SLF4J + logback 记录和修复问题 |
用例管理 | Excel、XMind |
五、测试环境
环境项 | 配置 |
---|---|
操作系统 | Windows / Linux |
数据库 | MySQL |
Java版本 | JDK 17 |
后端框架 | Spring Boot3 |
缓存 | Redis |
消息队列 | RabbitMQ |
日志框架 | SLF4J + Logback |
应用部署 | 阿里云服务器部署 |
测试工具版本 | Postman v10、Selenium 4.0 |
六、测试用例统计
七、缺陷分析(Bug记录)
Bug编号 | 模块 | 严重程度 | 问题描述 | 状态 |
---|---|---|---|---|
BUG-001 | 抽奖模块 | 高 | (示例) 极端并发情况下,可能出现一人中多次奖的情况 | 待复测 |
BUG-002 | 通知模块 | 中 | (示例) 邮件通知发送失败时,未提供重试机制 | 待修复 |
BUG-003 | 登录模块 | 中 | (示例) JWT token 过期后,前端未及时引导用户重新登录 | 待修复 |
BUG-004 | 异步处理 | 低 | (示例) 死信队列消息处理后,日志记录不够详细 | 已修复 |
八、结论与建议
✅ 测试结论:
抽奖系统核心功能基本稳定,能够支持基本的抽奖活动需求。异步抽奖设计有效提升了用户体验,事务一致性和消息可靠性通过回滚和死信队列得到保障。身份验证和数据加密措施在一定程度上保障了系统安全。
🔧 建议:
- 加强并发测试: 针对抽奖场景进行更严格的并发测试,确保在高并发下抽奖公平性和唯一性的持续有效。
- 优化消息通知的重试机制: 进一步完善短信和邮件通知的重试策略和失败告警机制,确保通知的送达率。
- 提升前端错误处理和用户体验: 针对异步抽奖接口返回成功后前端的展示,可以增加更丰富的兜底方案,例如在数据未完全落库前提供加载状态或更友好的提示。
- 探索更全面的安全测试: 考虑引入自动化安全扫描工具,进一步发现潜在的安全漏洞,如XSS、CSRF等。
- 完善日志监控和告警: 针对异步处理、死信队列等关键流程,设置更细致的日志级别和告警,便于及时发现和解决问题。
九、附录
测试用例Excel文档 (部分)
Postman 接口测试集合
项目部署文档
*部分代码展示:
package tests;
import common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;
public class BloginTest extends Utils {
public static String url = "http://60.205.7.136:8082/blogin.html";
public BloginTest() {};
public void LoginTest() throws InterruptedException {
driver.get(url);
// 首先验证标题
String expectedTitle = driver.getTitle();
assert expectedTitle.equals("管理员登录界面");
// 模拟登陆测试
driver.findElement(By.cssSelector("#phoneNumber")).sendKeys("18039295275");
driver.findElement(By.cssSelector("#password")).sendKeys("123456");
driver.findElement(By.cssSelector("#loginForm > button")).click();
Thread.sleep(1000);
String expectedTitle1 = driver.getTitle();
System.out.println(expectedTitle1);
assert expectedTitle1.equals("后台官迷");
System.out.println("模拟登陆成功!");
}
// 检查是否加载成功
public void LoginRight() throws InterruptedException {
driver.get(url);
// 手机号 + 密码登录
// 检查是否有 user和 password
driver.findElement(By.cssSelector("#phoneNumber")).sendKeys("18039295275");
driver.findElement(By.cssSelector("#password")).sendKeys("123456");
System.out.println("手机号+密码登录界面测试成功!");
// Thread.sleep(2000);
// 验证码登录
driver.findElement(By.cssSelector("body > div > div.login-container.col-sm-6.col-md-6.col-lg-5.col-xl-5 > div.tab-box > span:nth-child(2)")).click();
//Thread.sleep(1000);
driver.findElement(By.cssSelector("#loginMobile")).sendKeys("18039295275");
driver.findElement(By.cssSelector("#getVerificationCode"));
driver.findElement(By.cssSelector("#verificationCode")).sendKeys("123456");
System.out.println("手机号+验证码登录界面测试成功!");
}
// 正常登录 验证码登录由于无法使用阿里云短信服务故无法验证 --》直接看手机是否收到手机号
public void LoginSubmitRight() {
driver.findElement(By.cssSelector("body > div > div.login-container.col-sm-6.col-md-6.col-lg-5.col-xl-5 > div.tab-box > span.tab-span.active")).click();
driver.navigate().refresh();
// 手机号 + 密码登录
driver.findElement(By.cssSelector("#phoneNumber")).sendKeys("18039295275");
driver.findElement(By.cssSelector("#password")).sendKeys("123456");
driver.findElement(By.cssSelector("#loginForm > button")).click();
String expectedTitle1 = driver.getTitle();
assert expectedTitle1.equals("后台管理");
System.out.println("手机号+密码正常登录成功");
}
/**
* 登录失败测试--手机+密码登录
*/
public void LoginSubmitError1() throws InterruptedException {
driver.findElement(By.cssSelector("body > div.header-box > div.user-box > div > span")).click();
// 返回上一界面 因为上一个测试是“登录成功”,成功之后跳转到list界面了
// driver.navigate().back();
// driver.navigate().refresh();
Thread.sleep(2000);
// 用户名正确 + 密码错误
driver.findElement(By.cssSelector("#phoneNumber")).sendKeys("18039295275");
driver.findElement(By.cssSelector("#password")).sendKeys("123451");
driver.findElement(By.cssSelector("#loginForm > button")).click();
// 由于设置的有弹窗需要解决一下 alert只能通过显示等待获取
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
Alert alert = wait.until(ExpectedConditions.alertIsPresent());
String text = alert.getText();
assert text.equals("登录失败!密码错误");
alert.accept();
System.out.println("登录失败--用户名正确--密码错误--验证成功");
// driver.quit();
}
/**
* 登录失败测试--用户名 || 密码为空
*/
public void LoginSubmitError2() throws InterruptedException {
// 刷新 重新输入
driver.navigate().refresh();
// Thread.sleep(3000);
driver.findElement(By.cssSelector("#phoneNumber")).sendKeys("");
driver.findElement(By.cssSelector("#password")).sendKeys("123451");
driver.findElement(By.cssSelector("#loginForm > button")).click();
String phoneNumberIsNULLText = driver.findElement(By.cssSelector("#phoneNumber-error")).getText();
phoneNumberIsNULLText.equals("请输入您的手机号");
System.out.println("登录失败测试--用户名 || 密码为空 验证成功");
}
/**
* 登录失败测试--检查是否存在sql注入问题 弹出"登录失败,密码或者用户名错误!"
*/
public void LoginSubmitError3() throws InterruptedException {
// 刷新 重新输入
driver.navigate().refresh();
// Thread.sleep(3000);
driver.findElement(By.cssSelector("#phoneNumber")).sendKeys("admin' -- ");
driver.findElement(By.cssSelector("#password")).sendKeys("123456");
driver.findElement(By.cssSelector("#loginForm > button")).click();
// 由于设置的有弹窗需要解决一下 alert只能通过显示等待获取
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(20));
Alert alert = wait.until(ExpectedConditions.alertIsPresent());
String text = alert.getText();
assert text.equals("登录失败!登录方式不存在");
alert.accept();
System.out.println("登录失败测试--检查是否存在sql注入问题--sql注入验证成功");
// driver.quit();
}
public void registerTest() {
driver.navigate().refresh();
driver.findElement(By.cssSelector("body > div > div.login-container.col-sm-6.col-md-6.col-lg-5.col-xl-5 > div.register-link > a")).click();
String expectedTitle = driver.getTitle();
assert expectedTitle.equals("注册页面");
System.out.println("成功进入注册页面");
driver.findElement(By.cssSelector("#name")).sendKeys("yj");
driver.findElement(By.cssSelector("#mail")).sendKeys("2314394022@qq.com");
driver.findElement(By.cssSelector("#phoneNumber")).sendKeys("18039295222");
driver.findElement(By.cssSelector("#password")).sendKeys("123456");
driver.findElement(By.cssSelector("#registerForm > button")).click();
System.out.println("注册成功!");
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
Alert alert = wait.until(ExpectedConditions.alertIsPresent());
alert.accept();
}
}
package tests;
import common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;
/**
* 创建活动接口测试
*/
public class CreateActivityTest extends Utils {
public static String url = "http://60.205.7.136:8082/admin.html";
public CreateActivityTest() {
}
public void createActivityRight() throws InterruptedException {
// 假设已经登录
driver.findElement(By.cssSelector("#createActivity")).click();
driver.switchTo().frame("contentFrame");
driver.findElement(By.cssSelector("#activityName")).sendKeys("test2");
driver.findElement(By.cssSelector("#description")).sendKeys("test2");
Thread.sleep(2000);
// 点击圈选奖品--跳出奖品选择模态框(重点是如何对模态框进行补货)
driver.findElement(By.cssSelector("#buttonPrizes")).click();
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(20));
WebElement prizeModal = wait.until(ExpectedConditions.visibilityOfElementLocated(
By.id("prizesModal")));
prizeModal.findElement(By.cssSelector("#prize-20")).click();// 模拟圈选一个奖品
prizeModal.findElement(By.cssSelector("#prizesModal > div > div.form-btn-box > button.btn.btn-primary")).click();
Thread.sleep(2000);
// 点击全选人员
driver.findElement(By.cssSelector("#buttonUsers")).click();
WebDriverWait wait1 = new WebDriverWait(driver, Duration.ofSeconds(20));
WebElement userModal = wait1.until(ExpectedConditions.visibilityOfElementLocated(
By.id("usersModal")
));
userModal.findElement(By.cssSelector("#user-41")).click();
userModal.findElement(By.cssSelector("#usersModal > div > div.form-btn-box > button.btn.btn-primary")).click();
Thread.sleep(2000);
driver.findElement(By.cssSelector("#createActivity")).click();
Alert alert = wait1.until(ExpectedConditions.alertIsPresent());
alert.accept();
System.out.println("活动创建成功!");
}
}
package tests;
import common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;
public class CreatePrizeTest extends Utils {
public static String url = "http://60.205.7.136:8082/admin.html";
public CreatePrizeTest() {
}
public void createPrizeRight() {
// 假设已经登录
driver.findElement(By.cssSelector("body > div.cont-box > div.sidebar > ul > li:nth-child(2) > ul > li:nth-child(2) > a")).click();
driver.switchTo().frame("contentFrame");
driver.findElement(By.cssSelector("#prizeName")).sendKeys("华为手机");
driver.findElement(By.cssSelector("#price")).sendKeys("5000");
driver.findElement(By.cssSelector("#description")).sendKeys("遥遥领先");
WebElement element = driver.findElement(By.xpath("/html/body/div/div/div/div[2]/div/div/input"));
element.sendKeys("C:\\Users\\绿字\\Desktop\\抽奖系统\\OIP-C (1).jpg");
driver.findElement(By.cssSelector("body > div > button")).click();
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(20));
Alert alert = wait.until(ExpectedConditions.alertIsPresent());
alert.accept();
System.out.println("奖品上传成功");
}
}