声动心弦 - 校园音乐分享平台的数字交响-测试报告
🎧 声动心弦 - 校园音乐分享平台的数字交响-测试报告
项目名称:声动心弦 - 校园音乐分享平台的数字交响
测试人员:LvZi
测试时间:2025年5月20日 - 2025年5月21日
一、项目概述
该项目为基于 Spring Boot 构建的音乐播放系统,提供用户注册登录、音乐上传/播放、歌曲收藏与管理等功能。后端负责业务逻辑与数据库交互,前端支持用户交互操作。系统功能模块完整,具备良好的实用性。
二、测试目标
- 验证各模块功能是否符合需求。
- 确保系统在正常、异常及边界条件下均能稳定运行。
- 验证系统接口稳定性与响应正确性。
- 验证安全性场景(如:登录认证、SQL注入、未授权访问等)。
- 检查音乐上传/播放/收藏等核心业务是否流畅、稳定。
三、测试范围
模块名称 | 涵盖功能 |
---|---|
用户模块 | 登录、Session验证、加密登录 |
音乐管理模块 | 上传、查询、播放、删除、批量删除 |
收藏模块 | 收藏、取消收藏、收藏查询 |
接口测试 | 所有REST接口状态码与返回结构校验 |
权限校验 | Session检查、未登录操作限制 |
四、测试方法与工具
类型 | 工具或技术栈 |
---|---|
功能测试 | Postman + 手动测试 |
接口测试 | Postman |
自动化测试 | Selenium(登录+上传) |
安全性测试 | 手动 SQL 注入验证 |
用例管理 | Excel、XMind |
五、测试环境
环境项 | 配置 |
---|---|
操作系统 | Windows 11 / Ubuntu Server |
浏览器 | Chrome 123.0 |
数据库 | MySQL 8.0 |
Java版本 | JDK 1.8 |
应用部署 | Spring Boot 内嵌 Tomcat,部署于本地和远程服务器 |
测试工具版本 | Postman v10、Selenium 4.0 |
六、测试用例统计
测试类型 | 用例数 | 通过数 | 失败数 | 覆盖率(估算) |
---|---|---|---|---|
功能测试 | 60 | 58 | 2 | 95% |
接口测试 | 30 | 29 | 1 | 96% |
自动化测试 | 10 | 10 | 0 | 100% |
性能测试 | 5 | 5 | 0 | - |
安全性测试 | 5 | 4 | 1 | - |
总计 | 110 | 106 | 4 | 95%以上 |
七、缺陷分析(Bug记录)
Bug编号 | 模块 | 严重程度 | 问题描述 | 状态 |
---|---|---|---|---|
BUG-001 | 登录模块 | 高 | 未处理SQL注入,存在安全风险 | 已修复 |
BUG-002 | 上传模块 | 中 | 上传同名音乐未校验,导致重复上传 | 已修复 |
BUG-003 | 收藏模块 | 中 | 重复收藏未提示“已收藏” | 已修复 |
BUG-004 | 接口响应 | 低 | 收藏接口部分错误返回 status=-1 但 message=“成功” | 已修复 |
八、结论与建议
✅ 测试结论:
系统主要功能基本稳定,常规功能测试通过率高,接口返回一致性好,自动化测试执行正常,具备良好的用户体验。
🔧 建议:
- 增加音乐重复校验逻辑,防止多次上传同一文件。
- 完善异常处理结构,特别是 message 和 status 的一致性。
- 进一步加强安全性处理,如统一防止XSS、CSRF等攻击。
- 收藏模块可以优化为幂等接口,用户多次收藏返回一致响应。
九、附录
-
测试用例Excel文档(部分)
-
相关代码:https://github.com/mylvzi/onlineMusicTest/tree/main
*部分代码展示:
/*** 存储所有测试用例公共部分*/
public class Utils {// 构造方法 每一个页面都要调用这个类 所以必须要把每个页面的url传输过来public Utils() {// 1.调用driver对象driver = creatDriver();}// 公用driver对象public static WebDriver driver = null;/*** 1.创建驱动对象* 写成一个单例模式 此处采用饿汉模式*/public static WebDriver creatDriver() {if(driver == null) {WebDriverManager.chromedriver().setup();ChromeOptions options = new ChromeOptions();// 允许访问所有的链接options.addArguments("--remote-allow-origins=*");Map<String, Object> prefs = new HashMap<>();// 禁用 Chrome 的密码保存提示prefs.put("credentials_enable_service", false);prefs.put("profile.password_manager_enabled", false);// 禁用密码泄露检测(这是关键)prefs.put("profile.password_manager_leak_detection", false);options.setExperimentalOption("prefs", prefs);// 尝试添加其他禁用安全特性的参数,但主要依赖上面的 prefsoptions.addArguments("--disable-features=PasswordLeakDetection,AutofillServerCommunication,SafeBrowseEnhancedProtection,SafeBrowse");options.addArguments("--disable-sync"); // 有时候同步设置也会影响options.addArguments("--no-default-browser-check"); // 禁用默认浏览器检查driver = new ChromeDriver(options);// 设置全局的隐式等待driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(5));}// System.out.println("驱动对象创建成功!");return driver;}/*** 2.屏幕截图*/public static void getScreenShot(String str) throws IOException {System.out.println("开始截图");// 设置文件格式// ./src/test/img/2025-5-25/test01-HHmmmss.pngSimpleDateFormat sim1 = new SimpleDateFormat("yyyy-MM-dd");SimpleDateFormat sim2 = new SimpleDateFormat("HH-mm-ss-SS");// 精确到毫秒// 使用时间戳格式化String dirTime = sim1.format(System.currentTimeMillis());String fileTime = sim2.format(System.currentTimeMillis());// 设置保存文件格式String fileName = "./src/test/img/" + dirTime + "/" + str + "-" + fileTime + ".png";// 截图File srcPng = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);FileUtils.copyFile(srcPng, new File(fileName));System.out.println("截图结束");}}public class LovemusicTest extends Utils {public static String url = "http://60.205.7.136:8080/loveMusic.html";public LovemusicTest() {super();}/*** 收藏音乐界面测试*/public void lovemusicTestUI() {driver.get(url);driver.findElement(By.cssSelector("#info > tr > td:nth-child(4) > button"));String expectedText = driver.findElement(By.cssSelector("body > div.container > h3")).getText();assert expectedText.equals("我喜欢的音乐列表");System.out.println("收藏音乐界面UI测试成功");}/*** 收藏音乐界面--功能测试**/public void lovemusicFunTest() throws IOException, InterruptedException {// 1.判断搜索功能是否正确driver.findElement(By.cssSelector("#exampleInputName2")).sendKeys("无能为力");driver.findElement(By.cssSelector("#submit1")).click();System.out.println("收藏页面--搜索功能正常");getScreenShot("lovemusicFunTest");// 2.判断能否回到首页driver.findElement(By.cssSelector("body > div.container > div:nth-child(3) > a")).click();String expectedTitle = driver.getTitle();assert expectedTitle.equals("在线音乐服务器");Thread.sleep(2000);getScreenShot("lovemusicFunTest");System.out.println("收藏页面--回到首页功能正常");driver.quit();}
}public class LoginTest extends Utils {public static String url = "http://60.205.7.136:8080/login.html";public LoginTest() {super();}// 检查是否加载成功public void LoginRight() {driver.get(url);// 检查是否有 user和 passworddriver.findElement(By.cssSelector("#user"));driver.findElement(By.cssSelector("#password"));}// 正常登录public void LoginSubmitRight() {driver.get(url);// 1.输入账号密码driver.findElement(By.cssSelector("#user")).sendKeys("bit");driver.findElement(By.cssSelector("#password")).sendKeys("123456");driver.findElement(By.cssSelector("#submit")).click();// 由于设置的有弹窗需要解决一下 alert只能通过显示等待获取WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));Alert alert = wait.until(ExpectedConditions.alertIsPresent());alert.accept();// 2.检查url是否是/listString expectedTitle = driver.getTitle();System.out.println(expectedTitle);assert expectedTitle.equals("在线音乐服务器");// 3.检查url是否是/listString currentUrl = driver.getCurrentUrl();System.out.println(currentUrl);assert currentUrl.equals("http://60.205.7.136:8080/list.html");// 4.检查是否含有list页面的“查询按钮”元素driver.findElement(By.cssSelector("#submit1"));// 如果找不到就会报异常// driver.quit();}/*** 登录失败测试--用户名/密码输入错误*/public void LoginSubmitError1() throws InterruptedException {// 返回上一界面 因为上一个测试是“登录成功”,成功之后跳转到list界面了driver.navigate().back();driver.navigate().refresh();// Thread.sleep(3000);driver.findElement(By.cssSelector("#user")).sendKeys("bit1");driver.findElement(By.cssSelector("#password")).sendKeys("123456");driver.findElement(By.cssSelector("#submit")).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("#user")).sendKeys("");driver.findElement(By.cssSelector("#password")).sendKeys("123456");driver.findElement(By.cssSelector("#submit")).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();}/*** 登录失败测试--检查是否存在sql注入问题 弹出"登录失败,密码或者用户名错误!"*/public void LoginSubmitError3() throws InterruptedException {// 刷新 重新输入driver.navigate().refresh();// Thread.sleep(3000);driver.findElement(By.cssSelector("#user")).sendKeys("admin' -- ");driver.findElement(By.cssSelector("#password")).sendKeys("123456");driver.findElement(By.cssSelector("#submit")).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("sql注入验证成功");// driver.quit();}
}
截图测试