测试报告-博客系统

目录

一、项目背景

二、功能简介

三、测试分类

3.1 功能测试

1)测试用例

2)功能测试结果

3.2 自动化测试 

1)自动化测试覆盖模块

2)编写Web测试用例

3)代码编写

4)结果分析

3.3 测试总结


一、项目背景

  1. 为了满足个人和小型团队快速搭建博客的需求,我开发了一个简易博客系统。该系统采用轻量级技术栈,注重易用性和快速部署,让用户无需复杂配置即可轻松发布和管理文章。支持Markdown写作,同时保持代码简洁、易于二次开发。无论是技术分享、生活记录还是知识整理,这个博客系统都能提供简单高效的解决方案。
  2. 项目使用了前后端分离的方法实现,前后端数据使用ajax进行交互,后端使用 Spring MVC 和 MyBatis 进行开发。
  3. 该项目一共有四个页面:博客登陆页、博客列表页、博客详情页、博客编辑页。一共实现了登录、注销、发布博客、编辑博客、删除博客、拦截器强制登录等功能。

二、功能简介

本项目一共实现了登录、注销、发布博客、编辑博客、删除博客、拦截器强制登录等功能:

  1. 登录功能:本项目是以个人和小型团队为目标群体进行开发的,因此没有做注册功能,用户的用户名和密码是已经存在的写在数据里的。当用户正确输入用户名和密码时,点击登录即可跳转至博客列表页。用户在未登录状态下,点击主页和写博客中的“发布文章”则会被拦截至登录页面。
  2. 注销功能:登录后,页面导航栏区会出现注销按钮,点击注销则会删除user_token信息,想要再次访问其他页面时由于token验证失败则会拦截至登录页进行重新登陆。
  3. 发布/编辑博客:未登录状态下,进入写博客页面可以正常编辑文章内容,但是点击发布文章时会发布失败并且提醒当前状态未登录。登录状态下,发布博客必须确保标题和内容不为空才可以正常发布。发布成功会跳转至博客列表页,在博客列表页可以看到博客标题、发布时间以及博客内容。
  4. 删除/编辑博客:用户在博客列表页可以看到团队成员发布的博客信息,点击博客即可进入博客详情页,进入博客详情页后,后端实现了获取作者信息的功能,前端进行验证当前登录用户是否为作者,如果当前登录用户是该文章的作者,则前端页面会在文章下面展示出“编辑”和“删除”按钮,反之则不会展示,只能浏览不能操作。

三、测试分类

3.1 功能测试

1)测试用例

2)功能测试结果

1.登录成功:输入正确的用户名和密码,点击登录,跳转至博客列表页

结果与预期结果一致 。

登录失败:输入错误的用户名或密码、用户名或密码为空都会登录失败,跳转至loginerr页

结果与预期结果一致。

2. 写博客:输入完整的标题和内容,点击发表文章,发布成功-跳转至博客列表页

结果与预期结果一致。

标题为空或者内容为空都无法发布

结果与预期结果一致。

3. 编辑博客/删除博客:只有身份匹配才会出现按钮

结果与预期结果一致。

点击删除则会把这篇标题为“Hello World”的博客删除并返回博客列表页

结果与预期结果一致。

4.注销:点击“注销”跳转至博客登录页

结果与预期结果一致。

5.测试结果:测试用例100%通过 


3.2 自动化测试 

1)自动化测试覆盖模块

该项目一共有四个页面:博客登陆页、博客列表页、博客详情页、博客编辑页。一共实现了登录、注销、发布博客、编辑博客、删除博客、拦截器强制登录等功能。

本次自动化测试覆盖了:博客登陆页、博客列表页、博客详情页、博客编辑页。

2)编写Web测试用例

3)代码编写

1. 首先在pom.xml文件中导入所需的依赖:

    <dependencies>
        <!--驱动管理-->
        <dependency>
            <groupId>io.github.bonigarcia</groupId>
            <artifactId>webdrivermanager</artifactId>
            <version>5.8.0</version>
            <scope>test</scope>
        </dependency>
        <!--selenium库-->
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>4.0.0</version>
        </dependency>
        <!--屏幕截图-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
    </dependencies>

2.有一些代码需要频繁使用所以创建公共配置类Utils:接下来创建的测试类均继承该类。

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 java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.Duration;

public class Utils {
    public static WebDriver driver;

    public static WebDriver createDriver() {
        if (driver == null) {
            WebDriverManager.chromedriver().setup();
            ChromeOptions options = new ChromeOptions();
            options.addArguments("--remote-allow-origins=*");
            driver = new ChromeDriver(options);

            //隐式等待
            driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(2));
        }
        return driver;
    }

    public Utils(String url) {
        //调用driver对象
        driver = createDriver();
        //访问url
        driver.get(url);
    }

    public void getScreenShot() throws IOException {
        /*保存格式:
         *       ./src/test/image/
         *                      /2025-01-01/
         *                                 /test01-082530.png
         *                                 /test02-082530.png
         *                      /2025-01-02/
         *                                 /test01-082530.png
         *                                 /test02-082530.png
         * */
        String callerMethodName = Thread.currentThread().getStackTrace()[2].getMethodName();
        // 屏幕截图
        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/image/2025-01-01/test01-082530.png
        String filename = "./src/test/image/" + dirTime + "/" + callerMethodName + "-" + fileTime + ".png";

        File srcFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
        //srcFile放到指定的位置
        FileUtils.copyFile(srcFile, new File(filename));
    }
}

3.创建各个页面的测试类:

博客登陆页 LoginPage:

package tests;

import common.Utils;
import org.openqa.selenium.By;

import java.io.IOException;

public class LoginPage extends Utils {
    public static String url = "http://127.0.0.1:8080/blog_login.html";

    public LoginPage() {
        super(url);
    }

    /*
    检查页面是否加载成功
     */
    public void loginPageRight() throws InterruptedException {
        //通过查看页面元素是否存在来检查页面加载成功与否
        driver.findElement(By.cssSelector("body > div.nav > a:nth-child(4)"));
        //登录输入框
        driver.findElement(By.cssSelector("body > div.container-login > div"));
    }

    //检查登录功能---成功登录
    public void loginSuc() {
        //确保输入框内没有内容
        driver.findElement(By.cssSelector("#username")).clear();
        driver.findElement(By.cssSelector("#password")).clear();

        driver.findElement(By.cssSelector("#username")).sendKeys("闻人翊悬");
        driver.findElement(By.cssSelector("#password")).sendKeys("123456");
        driver.findElement(By.cssSelector("#submit")).click();
        //检查点击登录之后是否登录成功
        driver.findElement(By.cssSelector("body > div.container > div.left > div"));
        driver.findElement(By.cssSelector("body > div.nav > a:nth-child(6)"));
        //页面标题来检查是否登录成功
        String title = driver.getTitle();
        assert title.equals("博客列表页"); //断言需要配置 -ea -Dfile.encoding=UTF-8

        driver.navigate().back();
    }

    //检查登录功能---登录失败
    public void loginFail() throws IOException {
        //确保输入框内没有内容
        driver.findElement(By.cssSelector("#username")).clear();
        driver.findElement(By.cssSelector("#password")).clear();
        //driver.navigate().refresh();  //也可以通过刷新实现

        driver.findElement(By.cssSelector("#username")).sendKeys("闻人翊悬");
        driver.findElement(By.cssSelector("#password")).sendKeys("123");
        driver.findElement(By.cssSelector("#submit")).click();

        //登录失败会跳转到登录失败页面
        String res = driver.findElement(By.cssSelector("body")).getText();
        getScreenShot();
        assert res.equals("用户名或密码错误!");

    }
}

博客列表页 ListPage:

package tests;

import common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;

import java.io.IOException;

public class ListPage extends Utils {
    public static String url = "http://127.0.0.1:8080/blog_list.html";

    public ListPage() {
        super(url);
    }

    //登录状态下---访问列表页面
    public void listByLogin() throws IOException {
        //检查元素
        driver.findElement(By.cssSelector("body > div.container > div.right > div:nth-child(1) > a"));
        driver.findElement(By.cssSelector("body > div.container > div.right > div > div.date"));
        driver.findElement(By.cssSelector("body > div.nav > a:nth-child(6)"));
        getScreenShot();
    }

    //未登录状态下---访问列表页面
    public void listPageByNoLogin() throws IOException {
        //列表页未登录
        //处理弹窗警告
        Alert alert = driver.switchTo().alert();
        alert.accept();
        //跳转到登录页面
        String expect = driver.getTitle();
        getScreenShot();
        assert expect.equals("博客登陆页");
    }
}

博客编辑页 EditPage:

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 java.io.IOException;

public class EditPage extends Utils {
    public static String url = "http://127.0.0.1:8080/blog_edit.html";

    public EditPage() {
        super(url);
    }

    //写博客成功
    public void editSuc() throws IOException, InterruptedException {
        //填写标题
        driver.findElement(By.cssSelector("#title")).sendKeys("AutoTest01");

        //因为输入框使用的是第三方插件markdown,所以不能使用sendKeys输入内容
        //方法一:填写内容时,有默认内容可以不再填写
        //方法二: 通过键鼠操作来实现
        /*
            1.鼠标先挪动到博客内容区域
            2.双击鼠标将内容删掉:鼠标双击内容+键盘DELETE
            3.输入内容
         */
//        WebElement ele = driver.findElement(By.cssSelector("#editor > div.CodeMirror.cm-s-default.CodeMirror-wrap.CodeMirror-empty"));
//        Actions actions = new Actions(driver);
//        //perform作用:为了在页面看到效果
//        Thread.sleep(3000);
//        actions.doubleClick(ele).perform();
//        Thread.sleep(3000);
//        actions.keyDown(Keys.DELETE).perform();
//        actions.keyDown(ele,Keys.DELETE).perform();
//        Thread.sleep(3000);
//        actions.moveToElement(ele).sendKeys("键盘鼠标操作输入博客内容").perform();
//        Thread.sleep(3000);

        getScreenShot();
        //点击发布
        driver.findElement(By.cssSelector("#submit")).click();
        Thread.sleep(1000);
        //检查
        String title = driver.getTitle();
        assert title.equals("博客列表页");
    }

    //未登录状态下,写博客不会发表
    public void editPageByNoLogin() throws IOException {
        driver.findElement(By.cssSelector("#title")).sendKeys("博客标题");
        driver.findElement(By.cssSelector("#submit")).click();
        //处理弹窗警告
        Alert alert = driver.switchTo().alert();
        alert.accept();
        //跳转到登录页面
        String expect = driver.getTitle();
        getScreenShot();
        assert expect.equals("博客登陆页");
    }
}

博客详情页 DetailPage:

package tests;

import common.Utils;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;

import java.io.IOException;

public class DetailPage extends Utils {
    public static String url = "http://127.0.0.1:8080/blog_detail.html";
    public DetailPage() {
        super(url);
    }

    //登录状态下详情页可以打开,不加 blogId则会 弹窗:内部错误,请联系管理员
    public void detailSucNoId() throws IOException {
        //处理弹窗警告
        Alert alert = driver.switchTo().alert();
        alert.accept();

        String title = driver.getTitle();
        assert title.equals("博客详情页");
        getScreenShot();
        //返回主页
        driver.findElement(By.cssSelector("body > div.nav > a:nth-child(4)")).click();
    }

    //登录状态下详情页可以打开,并且加了 blogId
    public void detailSucById() throws IOException {
        driver.get("http://127.0.0.1:8080/blog_detail.html?blogId=1");
        driver.findElement(By.cssSelector("body > div.container > div.right > div > div.title"));
        driver.findElement(By.cssSelector("body > div.container > div.right > div > div.date"));
        driver.findElement(By.cssSelector("#detail > p"));
        getScreenShot();
    }

    //未登录状态下,访问博客详情页会拦截,跳转至登录页
    public void detailNoLogin() throws IOException {
        driver.findElement(By.cssSelector("body > div.container-login > div"));
        String title = driver.getTitle();
        assert title.equals("博客登陆页");
        getScreenShot();
    }
}

RunTest:

import common.Utils;
import tests.*;

import java.io.IOException;

public class RunTest {
    public static void main(String[] args) throws InterruptedException, IOException {

        // 未登录时自动化测试
        ListPage listPage = new ListPage();
        listPage.listPageByNoLogin();
        EditPage editPage = new EditPage();
        editPage.editPageByNoLogin();
        DetailPage detailPage = new DetailPage();
        detailPage.detailNoLogin();

        //登录状态下 自动化测试
        LoginPage loginPage = new LoginPage();
        loginPage.loginPageRight();
        loginPage.loginSuc();
        loginPage.loginFail();
        ListPage listPage1 = new ListPage();
        listPage1.listByLogin();
        EditPage editPage1 = new EditPage();
        editPage1.editSuc();
        DetailPage detailPage1 = new DetailPage();
        detailPage1.detailSucNoId();
        detailPage1.detailSucById();

        Utils.driver.quit();
    }
}

代码仓库地址:https://gitee.com/nan-yiriel-l/test/tree/master/code/BlogAutoTest

4)结果分析

本次代码自动化测试用例数量:10

自动化测试结果:pass:10/10、fail:0/10

自动化测试时遇到的问题及解决办法:

  • 登录状态的测试用例和未登录状态的测试用例穿插运行,导致部分测试用例之间衔接不上。解决方法是注意测试用例间的先后执行顺序
  • UnexpectedAlertPresentException异常。原因是没有处理弹窗,导致后续操作无法进行。解决方法是在进行后续操作之前先处理弹窗

3.3 测试总结

本次测试针对博客系统的核心功能进行了验证测试,包括用户登录、文章发布、文章编辑、文章删除、用户注销、强制登录登。通过功能测试,确认系统业务流程符合预期,界面交互流畅,关键功能点均能正常运行。同时,采用自动化测试覆盖了主要功能模块,确保系统稳定性和可靠性。整体测试结果表明,该博客系统主要功能完整、运行稳定,具备良好的用户体验和可维护性,已达到预期发布标准。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值