一、项目背景
1.1 项目概述
本项目旨在构建一个基于Spring Boot、MyBatis、MySQL及Spring MVC技术栈的二次元论坛,为用户提供一个集内容创作、分享、交流于一体的在线平台。该系统将充分利用Spring Boot的便捷性简化项目配置与部署,结合MyBatis的高效数据库操作能力,以及Spring MVC的强大页面交互处理能力,共同打造一个功能完善、性能优良的论坛。
1.2项目名称
二次云论坛
1.3开发语言
Java
1.4开发环境
IntelliJ IDEA 2022.2.2 (Ultimate Edition)
JDK1.8.0
1.5自动化测试代码
https://github.com/muyangyo/SynchroProject/tree/main/JavaProject/webAutoTestFrame/src/main/java
二、项目主要页面及功能
2.1用户登录页面
http://127.0.0.1:8080/login.html

2.2论坛列表页
http://127.0.0.1:8080/blog_list.html

2.3文章详情页
http://127.0.0.1:8080/blog_detail.html

2.4文章编辑页
http://127.0.0.1:8080/blog_edit.html

2.5文章修改页
http://127.0.0.1:8080/blog_modify.html

2.6注册页
http://127.0.0.1:8080/register.html

三、对项目进行测试
3.1编写测试用例
3.1.1用户登录页面测试用例

3.1.2论坛列表页面测试用例

3.1.3文章详情页测试用例

3.1.4文章编辑页测试用例

3.1.5文章修改页测试用例

3.1.6注册页测试用例

3.2执行测试
首先要创建一个maven项目,引入selenium以及Junit5的依赖以及安装浏览器驱动依赖
<!-- Selenium WebDriver dependency -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.0.0</version>
</dependency>
<!-- WebDriverManager for automatic WebDriver management -->
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>5.8.0</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.1</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.9.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-params -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.9.1</version>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite</artifactId>
<version>1.9.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.platform/junit-platform-suite -->
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite</artifactId>
<version>1.9.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.github.bonigarcia</groupId>
<artifactId>webdrivermanager</artifactId>
<version>5.8.0</version>
<scope>compile</scope>
</dependency>
创建Meta类,Sleep类,WaitStrategy枚举类,利用继承Meta类,简化代码,提高复用率
package Base;
import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import java.time.Duration;
/**
* 创建于 IntelliJ IDEA.
* 描述:
* User: 沐阳Yo
*/
public class Meta {
/**
* 为什么这里使用的是静态呢? 因为避免new对象才进行初始化,直接调用就进行初始化
*/
static {
//使⽤插件管理⼯具webdrivermanager
WebDriverManager.chromedriver().setup();
//添加浏览器配置
ChromeOptions options = new ChromeOptions();
//允许任何来源的远程连接
options.addArguments("--remote-allow-origins=*");
driver = new ChromeDriver(options);
}
@RequiresInitialization
public static void init(WaitStrategy strategy) {
if (strategy.equals(WaitStrategy.INTELLIGENT_WAITING)) {
//智能等待
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(2));//隐式等待2秒
}
sleep = new Sleep();
}
public static ChromeDriver driver;
public static Sleep sleep;
public static WebElement byCssGetElement(String cssSelector) {
return driver.findElement(By.cssSelector(cssSelector));
}
public static WebElement byXpathGetElement(String xpath) {
return driver.findElement(By.xpath(xpath));
}
}
package Base;
public class Sleep {
/**
* 使当前线程暂停执行3000毫秒(3秒)
*/
public void sleepLongTime() throws InterruptedException {
Thread.sleep(3000);
}
/**
* 使当前线程暂停执行1500毫秒(1.5秒)
*/
public void sleepNormalTime() throws InterruptedException {
Thread.sleep(1500);
}
/**
* 使当前线程暂停执行500毫秒(0.5秒)
*/
public void sleepShortTime() throws InterruptedException {
Thread.sleep(500);
}
}
package Base;
/**
* 创建于 IntelliJ IDEA.
* 描述:
* User: 沐阳Yo
*/
public enum WaitStrategy {
MANDATORY_WAIT, INTELLIGENT_WAITING;
}
再编写测试类 HubSystemTest 继承于 Meta 类,并且完成 Meta 类的初始化设置,和所有测试完成后的收尾工作
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class HubSystemTest extends Meta {
static int anInt = 0;
@BeforeAll
static void Init() throws IOException {
init(WaitStrategy.INTELLIGENT_WAITING);
//利用文件进行存储该代码运行了多少次,便于后续注册站账号时使用
File file = new File("./mark/mark.txt");
if (!file.exists()) {
FileUtils.touch(file);
}
try (InputStream inputStream = new FileInputStream(file);
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
Scanner scanner = new Scanner(inputStreamReader)) {
if (scanner.hasNextInt()) {
anInt = scanner.nextInt();
}
}
try (OutputStream outputStream = new FileOutputStream(file);
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);
PrintWriter printWriter = new PrintWriter(outputStreamWriter)) {
printWriter.println(anInt + 1);
printWriter.flush();
}
}
@AfterAll
static void close() {
driver.quit();
}
编写注册自动化测试
@ParameterizedTest
@CsvSource({"'user','user'"})
@Order(0)
void registerTest(String newUsername, String newPassword) throws InterruptedException {
newUsername = newUsername + anInt;
newPassword = newPassword + anInt;
driver.get("http://127.0.0.1:8080/");
WebElement registerBtn = byCssGetElement("body > div.login-container > div > div:nth-child(4) > a");
registerBtn.click();
sleep.sleepNormalTime();
Assertions.assertEquals("http://127.0.0.1:8080/register.html", driver.getCurrentUrl());
WebElement username = byCssGetElement("#inputUsername");
username.sendKeys(newUsername);
WebElement password = byCssGetElement("#inputPassword");
password.sendKeys(newPassword);
WebElement rePassword = byCssGetElement("#inputREPassword");
rePassword.sendKeys(newPassword + "1");
WebElement submit = byCssGetElement("#registerButton");
submit.click();
sleep.sleepNormalTime();
//验证如果密码不同是否会报错
Alert alert = driver.switchTo().alert();
Assertions.assertEquals("两次输入的密码不一致!", alert.getText());
alert.accept();
sleep.sleepNormalTime();
//重新输入密码
rePassword.clear();
rePassword.sendKeys(newPassword);
submit.click();
sleep.sleepNormalTime();
Alert alert1 = driver.switchTo().alert();
Assertions.assertEquals("注册成功!", alert1.getText());
alert1.accept();
}
编写登入自动化测试
@ParameterizedTest
@CsvFileSource(resources = "login.csv")
@Order(1)
void loginTest(String username, String password) throws InterruptedException {
driver.get("http://127.0.0.1:8080/");
WebElement usernameText = byCssGetElement("#username");
usernameText.sendKeys(username);
WebElement passwordText = byCssGetElement("#password");
passwordText.sendKeys(password);
WebElement submit = byCssGetElement("#submit");
submit.click();
sleep.sleepNormalTime();
driver.switchTo().alert().accept();
Assertions.assertEquals("http://127.0.0.1:8080/blog_list.html", driver.getCurrentUrl());
Assertions.assertEquals(username, byCssGetElement("body > div.container > div.container-left > div > h3").getText());
}
编写发布文章自动化测试
@Test
@Order(2)
void publishBlogTest() throws InterruptedException {
driver.get("http://127.0.0.1:8080/blog_list.html");
WebElement a = byCssGetElement("body > div.nav > a:nth-child(5)");
a.click();
sleep.sleepNormalTime();
WebElement title = byCssGetElement("#title-input");
title.sendKeys("自动化测试生成文章");
WebElement submit = byCssGetElement("#submit");
submit.click();
sleep.sleepShortTime();
Alert alert = driver.switchTo().alert();
Assertions.assertEquals("成功添加!", alert.getText());
alert.accept();
}
编写评论自动化测试
@Test
@Order(3)
void commentTest() throws InterruptedException {
driver.get("http://127.0.0.1:8080/blog_list.html");
WebElement blog = byXpathGetElement("//div[text()='自动化测试生成文章']");
blog.click();
sleep.sleepNormalTime();
WebElement text = byCssGetElement("body > div.container > div.container-left > div.commentArea > div > input");
text.sendKeys("测试评论是否正常");
WebElement btn = byCssGetElement("body > div.container > div.container-left > div.commentArea > div > button");
btn.click();
sleep.sleepNormalTime();
Alert alert = driver.switchTo().alert();
Assertions.assertEquals("成功添加评论!", alert.getText());
alert.accept();
sleep.sleepNormalTime();
driver.navigate().refresh();
sleep.sleepNormalTime();
WebElement webElement = byXpathGetElement("//p[text()='测试评论是否正常']");
}
编写修改文章自动化测试
@Test
@Order(4)
void editBlogTest() throws InterruptedException {
driver.get("http://127.0.0.1:8080/blog_list.html");
WebElement blog = byXpathGetElement("//div[text()='自动化测试生成文章']");
blog.click();
sleep.sleepNormalTime();
WebElement editBtn = byCssGetElement("body > div.container > div.container-left > div.card > button.edit-button");
editBtn.click();
sleep.sleepLongTime();
WebElement title = byCssGetElement("#title-input");
String text = title.getAttribute("value") + "修改后的";
title.clear();
title.sendKeys(text);
WebElement submit = byCssGetElement("#submit");
submit.click();
sleep.sleepNormalTime();
Alert alert = driver.switchTo().alert();
Assertions.assertEquals("成功修改!", alert.getText());
alert.accept();
sleep.sleepNormalTime();
WebElement newTitle = byCssGetElement("body > div.container > div.container-right > h3");
Assertions.assertEquals(text, newTitle.getText());
}
编写删除博客自动化测试
@Test
@Order(5)
void deleteBlogTest() throws InterruptedException {
driver.get("http://127.0.0.1:8080/blog_list.html");
WebElement blog = byXpathGetElement("//div[text()='自动化测试生成文章修改后的']");
blog.click();
sleep.sleepNormalTime();
WebElement deleteBtn = byCssGetElement("body > div.container > div.container-left > div.card > button.delete-button");
deleteBtn.click();
sleep.sleepShortTime();
Alert alert = driver.switchTo().alert();
Assertions.assertEquals("成功删除!", alert.getText());
alert.accept();
}
编写登出自动化测试
@Test
@Order(6)
void logoutTest() throws InterruptedException {
driver.get("http://127.0.0.1:8080/blog_list.html");
WebElement logoutBtn = byCssGetElement("body > div.nav > a:nth-child(6)");
logoutBtn.click();
sleep.sleepNormalTime();
Assertions.assertEquals("http://127.0.0.1:8080/login.html", driver.getCurrentUrl());
//未登入状态下进入首页
driver.get("http://127.0.0.1:8080/blog_list.html");
Assertions.assertEquals("请先登录!", driver.switchTo().alert().getText());
driver.switchTo().alert().accept();
Assertions.assertEquals("http://127.0.0.1:8080/login.html", driver.getCurrentUrl());
}
编写验证登录自动化测试
@ParameterizedTest
@CsvSource({"'Demo','123'"})
@Order(7)
void errorLoginTest(String username, String password) throws InterruptedException {
driver.get("http://127.0.0.1:8080/");
WebElement usernameText = byCssGetElement("#username");
usernameText.sendKeys(username);
WebElement passwordText = byCssGetElement("#password");
passwordText.sendKeys(password);
WebElement submit = byCssGetElement("#submit");
submit.click();
sleep.sleepNormalTime();
Alert alert = driver.switchTo().alert();
Assertions.assertEquals("登录失败! 账号或者密码错误!", alert.getText());
sleep.sleepNormalTime();
alert.accept();
}
三、测试总结
4.1测试分类
4.1.1功能测试
测试用例
功能测试结果:测试用例100%通过
4.1.2自动化测试
自动化测试模块覆盖:用户登录页面,用户注册页面,列表页面,文章详情页面,文章编辑页,文章修改页
自动化测试用例数量:8 个
自动化测试结果:

4.2遗留风险
测试时间紧张,先保证了Hub主体功能没有问题,可能存在细节测试不到位风险
4.3测试结果评估
(1) 部分页面未登入直接使用 URL 访问时,后端虽然检验权限不足,没有返回对应的数据,但是有的页面没有弹窗显示权限不足并自动跳转至登录页面
(2) 文章摘要在没有一或二级标题会无法生成
浙公网安备 33010602011771号