Roller: 抽奖系统测试的幕后剧本-测试报告

发布于:2025-06-02 ⋅ 阅读:(48) ⋅ 点赞:(0)

抽奖系统 - 测试报告

项目名称:抽奖系统
测试人员:LlvZi
测试时间:2025年5月25 - 2025年6月1


一、项目概述

该项目是一个操作简便、安全可靠的抽奖系统 。主要业务是抽奖,并支持管理员管理用户、奖品和抽奖活动,以配置抽奖内容。抽奖操作由管理员进行,支持依次抽取配置好的奖品和人数,并保证每人最多中一次奖 。系统对异常情况进行了处理,并对中奖人发送短信和邮件通知。项目采用了分层分模块设计,技术栈包括 Spring Boot3、JDK 17、MySQL、MyBatis、Redis、RabbitMQ、SLF4J + logback 等。


二、测试目标

  1. 验证抽奖系统各项核心功能(用户管理、奖品管理、活动管理、抽奖流程)是否符合需求。
  2. 确保系统在正常、异常及边界条件下均能稳定运行,特别是抽奖过程中的唯一性保证。
  3. 验证抽奖接口的异步处理及事务一致性。
  4. 验证系统安全性场景(如:身份验证、隐私数据加密)。
  5. 检查中奖通知(短信、邮件)的及时性和准确性。

三、测试范围

模块名称 涵盖功能
用户模块 注册、登录、信息管理、身份验证 (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

六、测试用例统计

| 测试类型   | 用例数    | 通过数    | 失败数    | 覆盖率(估算) |
| ------ | ------ | ------ | ------ | ------- |
| 功能测试   | XX     | XX     | XX     | XX%     |
| 接口测试   | XX     | XX     | XX     | XX%     |
| 单元测试   | XX     | XX     | XX     | XX%     |
| 安全性测试  | XX     | XX     | XX     | XX%     |
| **总计** | **XX** | **XX** | **XX** | **XX%** |


七、缺陷分析(Bug记录)

Bug编号 模块 严重程度 问题描述 状态
BUG-001 抽奖模块 (示例) 极端并发情况下,可能出现一人中多次奖的情况 待复测
BUG-002 通知模块 (示例) 邮件通知发送失败时,未提供重试机制 待修复
BUG-003 登录模块 (示例) JWT token 过期后,前端未及时引导用户重新登录 待修复
BUG-004 异步处理 (示例) 死信队列消息处理后,日志记录不够详细 已修复

八、结论与建议

✅ 测试结论:

抽奖系统核心功能基本稳定,能够支持基本的抽奖活动需求。异步抽奖设计有效提升了用户体验,事务一致性和消息可靠性通过回滚和死信队列得到保障。身份验证和数据加密措施在一定程度上保障了系统安全。

🔧 建议:

  1. 加强并发测试: 针对抽奖场景进行更严格的并发测试,确保在高并发下抽奖公平性和唯一性的持续有效。
  2. 优化消息通知的重试机制: 进一步完善短信和邮件通知的重试策略和失败告警机制,确保通知的送达率。
  3. 提升前端错误处理和用户体验: 针对异步抽奖接口返回成功后前端的展示,可以增加更丰富的兜底方案,例如在数据未完全落库前提供加载状态或更友好的提示。
  4. 探索更全面的安全测试: 考虑引入自动化安全扫描工具,进一步发现潜在的安全漏洞,如XSS、CSRF等。
  5. 完善日志监控和告警: 针对异步处理、死信队列等关键流程,设置更细致的日志级别和告警,便于及时发现和解决问题。

九、附录

  • 测试用例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("奖品上传成功");
    }

}


网站公告

今日签到

点亮在社区的每一天
去签到