博客系统测试报告

一、项目背景

1. 核心功能模块

  • 内容创作平台需求:随着互联网发展,个人与企业对内容发布、知识分享的需求日益增长,需要一个便捷的博客系统实现文章发布、管理与展示。
  • 学习与实践场景:作为 SSM(Spring+Spring MVC+MyBatis)框架的综合实践项目,旨在通过完整的项目开发流程,掌握企业级 Java 应用的设计与实现。
  • 技术整合需求:结合前端页面交互与后端业务逻辑,实现前后端数据交互、用户认证、数据持久化等核心功能,提升全栈开发能力。

2. 技术发展背景

  • 框架技术普及:SSM 框架在企业级应用中广泛使用,通过本项目可深入理解 Spring 的 IOC/DI、AOP,MyBatis 的 ORM 映射等核心技术。
  • 安全认证升级:采用 JWT(JSON Web Token)替代传统 Session 认证,解决集群环境下的会话管理问题,提升系统安全性与可扩展性。
  • 前后端分离趋势:通过 RESTful API 实现前后端数据交互,为后续向前后端分离架构升级奠定基础。

3. 教学与实践目标

  • 知识整合:巩固 Spring 框架、MyBatis 数据库操作、Web 交互等核心知识点,实现从理论到实践的转化。
  • 项目流程实践:覆盖需求分析、数据库设计、代码开发、测试部署等完整开发流程,培养工程化思维。
  • 问题解决能力:通过处理令牌认证、密码加密、异常处理等实际场景问题,提升系统设计与调试能力。

二、项目功能介绍

(1)用户管理模块

  • 用户注册与登录:实现用户名密码验证,使用 JWT 令牌生成与校验机制,确保认证安全。
  • 身份认证与授权:通过令牌拦截器实现登录状态校验,未登录用户访问受限页面自动跳转至登录页。
  • 密码安全机制:采用加盐 MD5 加密存储用户密码,防止明文泄露,提升数据安全性。

(2)博客内容管理

  • 博客发布与编辑:支持 Markdown 格式内容输入,通过 editor.md 编辑器实现富文本编辑与格式渲染。
  • 博客查询与展示:实现博客列表分页展示、详情页内容渲染,支持按时间倒序排列。
  • 博客修改与删除:作者可对自己的博客进行编辑或逻辑删除(标记 delete_flag),非作者无操作权限。

(3)交互与权限控制

  • 操作权限校验:根据用户身份(作者 / 非作者)动态显示编辑 / 删除按钮,防止越权操作。
  • 前端交互逻辑:通过 AJAX 实现前后端数据异步交互,动态渲染页面内容,提升用户体验。
  • 异常处理机制:统一处理参数校验失败、权限不足等异常,返回标准化错误响应。

三、用例设计

四、自动化UI测试

工具类

package common;

import io.github.bonigarcia.wdm.WebDriverManager;
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 org.openqa.selenium.support.ui.WebDriverWait;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.Duration;

public class Utils {


    public static WebDriver driver;
    public static String url;
    public WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(3));



    public Utils(String url) {
        driver = createDriver();
        wait = new WebDriverWait(driver, Duration.ofSeconds(3));
        driver.get(url);
    }


    /**
     * 创建浏览器驱动
     * @return
     */
    public static WebDriver createDriver() {
        if (driver != null) {
            return driver;
        }

        //下载谷歌浏览器驱动
        WebDriverManager.chromedriver().setup();

        //设置配置
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--remote-allow-origins=*");

        driver = new ChromeDriver(options);

        return driver;
    }

    /**
     * 屏幕截图
     */
    public static void takeScreenShot(String str) throws IOException {
        /**
         * 图片存放的路径
         * .src/test/java/images/2025-5-15/test_01_时间戳.png
         *                                /test_02_时间戳.png
         *                      /2025-5-16/test_01_时间戳.png
         */
        SimpleDateFormat sim1 = new SimpleDateFormat("yyyy-MM-dd");
        SimpleDateFormat sim2 = new SimpleDateFormat("HHmmssSS");

        String dirTime = sim1.format(System.currentTimeMillis());
        String fileTime = sim2.format(System.currentTimeMillis());
        //.src/test/java/images/2025-5-15/test_01_时间戳.png
        String fileName = "./src/test/java/images/" + dirTime + "/" + str + "_" + fileTime + ".png";

        //屏幕截图
        File stcFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
        FileUtils.copyFile(stcFile, new File(fileName));
    }


}
package tests;

import common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.ExpectedConditions;


public class LoginPage extends Utils {

    public static String url = "http://localhost:8081/blog_login.html";

    public LoginPage() {
        super(url);
    }

    /**
     * 检查页面是否可以正确打开
     */
    public void checkLoginPage() {

        String title = driver.getTitle();
        //测试标题是否可以正确显示
        String text = driver.findElement(By.xpath("/html/body/div[1]/span")).getText();

        String loginText = driver.findElement(By.xpath("//*[@id=\"loginForm\"]/h3")).getText();

        //登录输入框是否正确加载
        driver.findElement(By.xpath("//*[@id=\"username\"]"));
        driver.findElement(By.xpath("//*[@id=\"password\"]"));

        assert title.equals("博集云");
        assert text.equals("博集云");
        assert loginText.equals("登录");


    }

    /**
     * 检查异常情况
     * 账号:zhangsan 密码:空
     * 账号:空,密码:123456
     * 账号:不足3位,密码:123456
     * 账号:超过20位,密码:123456
     * 账号:zhangsan,密码:少于6个字符
     * 账号:超过20位,密码:超过20位
     * 账号:空,密码:空
     */
    public void checkLoginException() throws InterruptedException {

        String currentWindow = driver.getWindowHandle();

        //账号:zhangsan 密码:空
        driver.findElement(By.xpath("//*[@id=\"username\"]")).sendKeys("zhangsan");
        driver.findElement(By.xpath("//*[@id=\"submit\"]")).click();

        Alert alert1 = driver.switchTo().alert();
        assert alert1.getText().equals("密码不能为空!");
        alert1.dismiss();

        //账号:空,密码:123456
        driver.findElement(By.xpath("//*[@id=\"username\"]")).clear();
        driver.findElement(By.xpath("//*[@id=\"password\"]")).sendKeys("123456");
        driver.findElement(By.xpath("//*[@id=\"submit\"]")).click();
//        driver.switchTo().window(currentWindow);
        Alert alert2 = driver.switchTo().alert();
        assert alert2.getText().equals("用户名不能为空!");
        alert2.dismiss();

        //账号:不足3位,密码:123456
        driver.findElement(By.xpath("//*[@id=\"password\"]")).clear();
        driver.findElement(By.xpath("//*[@id=\"username\"]")).sendKeys("zh");
        driver.findElement(By.xpath("//*[@id=\"password\"]")).sendKeys("123456");
        driver.findElement(By.xpath("//*[@id=\"submit\"]")).click();
        Alert alert3 = driver.switchTo().alert();
        assert alert3.getText().equals("用户名长度不能少于3个字符!");
        alert3.dismiss();

        //账号:超过20位,密码:123456
        driver.findElement(By.xpath("//*[@id=\"username\"]")).clear();
        driver.findElement(By.xpath("//*[@id=\"password\"]")).clear();
        driver.findElement(By.xpath("//*[@id=\"username\"]")).sendKeys("123456789101111111111");
        driver.findElement(By.xpath("//*[@id=\"password\"]")).sendKeys("123456");
        driver.findElement(By.xpath("//*[@id=\"submit\"]")).click();
        Alert alert4 = driver.switchTo().alert();
        assert alert4.getText().equals("用户名长度不能超过20个字符!");
        alert4.dismiss();

        //账号:zhangsan,密码:少于6个字符
        driver.findElement(By.xpath("//*[@id=\"username\"]")).clear();
        driver.findElement(By.xpath("//*[@id=\"password\"]")).clear();
        driver.findElement(By.xpath("//*[@id=\"username\"]")).sendKeys("zhangsan");
        driver.findElement(By.xpath("//*[@id=\"password\"]")).sendKeys("12345");
        driver.findElement(By.xpath("//*[@id=\"submit\"]")).click();
        Alert alert5 = driver.switchTo().alert();
        assert alert5.getText().equals("密码长度不能少于6个字符!");
        alert5.dismiss();

        //账号:超过20位,密码:超过20位
        driver.findElement(By.xpath("//*[@id=\"username\"]")).clear();
        driver.findElement(By.xpath("//*[@id=\"password\"]")).clear();
        driver.findElement(By.xpath("//*[@id=\"username\"]")).sendKeys("123456789101111111111");
        driver.findElement(By.xpath("//*[@id=\"password\"]")).sendKeys("123456789101111111111");
        driver.findElement(By.xpath("//*[@id=\"submit\"]")).click();
        Alert alert6 = driver.switchTo().alert();
        assert alert6.getText().equals("用户名长度不能超过20个字符!");
        alert6.dismiss();

        //账号:空,密码:空
        driver.findElement(By.xpath("//*[@id=\"username\"]")).clear();
        driver.findElement(By.xpath("//*[@id=\"password\"]")).clear();
        driver.findElement(By.xpath("//*[@id=\"submit\"]")).click();
        Alert alert7 = driver.switchTo().alert();
        assert alert7.getText().equals("用户名不能为空!");
        alert7.dismiss();

    }

    /**
     *  测试正常登录的情况
     */
    public String loginSuccess(String userName, String password) {
        driver.findElement(By.xpath("//*[@id=\"username\"]")).clear();
        driver.findElement(By.xpath("//*[@id=\"password\"]")).clear();

        //检查导航栏是否正常显示
        driver.findElement(By.xpath("//*[@id=\"username\"]")).sendKeys(userName);
        driver.findElement(By.xpath("//*[@id=\"password\"]")).sendKeys(password);
        driver.findElement(By.xpath("//*[@id=\"submit\"]")).click();

        //检查是否正确加载主页
        wait.until(ExpectedConditions.elementToBeClickable(By.xpath("/html/body/div[1]/a[3]")));
        driver.findElement(By.xpath("/html/body/div[1]/a[3]"));

        return userName;
    }


}
package tests;

import common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.ExpectedConditions;

public class ListPage extends Utils {

    private static String url = "http://localhost:8081/blog_list.html";

    public ListPage() {
        super(url);
    }

    /**
     * 未登录情况 - 弹窗提示 - 进入登录页面
     */
    public void checkListPageNotLogin() {
        //处理弹窗
        Alert alert = driver.switchTo().alert();
        alert.accept();

        //判断是否回到登录页面
        String loginText = driver.findElement(By.xpath("//*[@id=\"loginForm\"]/h3")).getText();
        driver.findElement(By.xpath("//*[@id=\"username\"]"));
        driver.findElement(By.xpath("//*[@id=\"password\"]"));

        assert loginText.equals("登录");
    }

    /**
     * 登录成功 -
     */
    public void checkListPageSuccess(String userName) {
        //检查是否正确加载主页
        wait.until(ExpectedConditions.elementToBeClickable(By.xpath("/html/body/div[1]/a[1]")));
        wait.until(ExpectedConditions.elementToBeClickable(By.xpath("/html/body/div[1]/a[2]")));
        wait.until(ExpectedConditions.elementToBeClickable(By.xpath("/html/body/div[1]/a[3]")));
        driver.findElement(By.xpath("/html/body/div[1]/a[1]"));
        driver.findElement(By.xpath("/html/body/div[1]/a[2]"));
        driver.findElement(By.xpath("/html/body/div[1]/a[3]"));
        String name = driver.findElement(By.xpath("/html/body/div[2]/div[1]/div/h3")).getText();

        assert name.equals(userName);
    }


}
package tests;

import common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.ui.ExpectedConditions;

public class EditPage extends Utils {

    public static String url = "http://localhost:8081/blog_edit.html";

    public EditPage() {
        super(url);
    }

    /**
     *  不写标题
     */
    public void EditPageFail_noTitle() {
        //本来就不带标题
        driver.findElement(By.xpath("//*[@id=\"submit\"]")).click();

        Alert alert = driver.switchTo().alert();
        assert alert.getText().equals("标题和内容不能为空!");
        alert.accept();
    }

    /**
     * 不写内容
     */
    public void EditPageFail_noContent() throws InterruptedException {
        //写标题
        driver.findElement(By.cssSelector("#title")).sendKeys("自动化测试演示标题");
        Thread.sleep(1000);
        //清空内容
        WebElement ele = 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"));

        Actions action = new Actions(driver);
        Thread.sleep(2000);
        action.keyDown(Keys.ALT)
                .sendKeys(ele,Keys.ARROW_RIGHT) //把鼠标放到最右边
                .keyUp(Keys.ALT)  //通过ctrl+shift+↑选中全部文本
                .keyDown(Keys.SHIFT)
                .sendKeys(Keys.ARROW_UP)
                .sendKeys(Keys.DELETE)//删除选中的本操作
                .perform();

        //还没有执行完删除就提交了---必须要加上强制等待
        Thread.sleep(1000);
        driver.findElement(By.cssSelector("#submit")).click();
        //处理弹窗
        wait.until(ExpectedConditions.alertIsPresent());
        //切换弹窗
        Alert alert = driver.switchTo().alert();
        //点击确定——停留在当前页面
        alert.accept();

    }


}
package tests;

import common.Utils;
import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.ExpectedConditions;

public class DetailPage extends Utils {
    //从列表页进入
    public static String url = "http://localhost:8081/blog_list.html";

    public DetailPage() {
        super(url);
    }

    public DetailPage(String url) {
        super(url);
    }

    /**
     * 页面是否能正确显示
     */
    public void checkDetailPage() {
        //列表页进入
        wait.until(ExpectedConditions.elementToBeClickable(By.xpath("/html/body/div[2]/div[2]/div[1]/a")));
        driver.findElement(By.xpath("/html/body/div[2]/div[2]/div[1]/a")).click();

        wait.until(ExpectedConditions.elementToBeClickable(By.xpath("/html/body/div[2]/div[2]/div/div[1]")));
        //标题
        String titleText = driver.findElement(By.xpath("/html/body/div[2]/div[2]/div/div[1]")).getText();
        //时间
        String timeText = driver.findElement(By.xpath("/html/body/div[2]/div[2]/div/div[2]")).getText();
        //内容
        String contentText = driver.findElement(By.xpath("//*[@id=\"detail\"]")).getText();

        assert !titleText.isEmpty();
        assert !timeText.isEmpty();
        assert !contentText.isEmpty();

    }

    /**
     * 未登录的情况下
     */
    public void checkDetailPageNotLogin() {
        //处理弹窗
        driver.switchTo().alert().accept();
        //判断是否回到了登录页面
        String loginText = driver.findElement(By.xpath("//*[@id=\"loginForm\"]/h3")).getText();

        assert loginText.equals("登录");
    }


}
import tests.DetailPage;
import tests.EditPage;
import tests.ListPage;
import tests.LoginPage;

public class RunTests {

    public static void main(String[] args) throws InterruptedException {
        //先检查未登录的页面
        DetailPage detailPage1 = new DetailPage();
        detailPage1.checkDetailPageNotLogin();

        //登录页面
        LoginPage loginPage = new LoginPage();
        loginPage.checkLoginPage();
        loginPage.checkLoginException();

        //列表页面
        ListPage listPage = new ListPage();
        listPage.checkListPageNotLogin();
        String userName = loginPage.loginSuccess("zhangsan", "123456");

        //详情页面
        DetailPage detailPage = new DetailPage();
        detailPage.checkDetailPage();

        //编辑页面
        EditPage editPage = new EditPage();
        editPage.EditPageFail_noTitle();
        editPage.EditPageFail_noContent();

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值