1、项目背景
1.1技术背景
1)个人博客系统主要是通过前端(HTML+css+js)+后端(SpringBoot)实现的一个博客的基本功能。前端通过jQuery的方式向后端请求数据。后端通过MyBatis从数据库中查询数据响应给前端。
2)项目中,用户的密码使用md5的加盐算法进行加密处理,以保证密码的安全性。
3)数据封装成统一的返回格式,便于前后端交互。
1.2功能背景
1)注册:新用户进行注册,后端会进行校验,如果注册的用户已存在,会弹窗提示用户已存在,如果两次密码不一致,会提示用户重新输入;如果注册成功,会跳转到登录页面。
2)登录:用户输入用户名和密码,登录成功,跳转到个人博客列表页,显示当前用户已发布的博客信息。
3)发布博客:点击发布博客,会跳转到博客添加页,输入博客内容,发布博客,然后跳转到博客列表页,展示刚刚发布的博客。
4)博客详情页:点击查看全文,跳转到博客详情页,展示了博客的标题、内容、发布时间、阅读量、作者信息(作者用户名、作者发布文章数)。
5)博客列表页:显示所有用户发布的博客,会以分页的形式展示,我设置的默认是每页显示两条,会有总页数,当前所在页。
6)修改博客:在个人博客列表中,可以点击修改某一篇文章,进入博客修改页重新进行编辑,然后发布修改。
7)删除博客:在个人博客列表中,可以点击删除某一篇文章。
8)游客登录:如果用户未登录,作为游客可以访问博客列表页(主页),可以查看所有用户发布的博客内容及博客详情,但是不能发布文章。
9)注销:退出当前账号,回到登录页面。
2、测试用例编写
3、自动化测试
3.1、准备工作
1)创建Maven项目,导入相关依赖
<dependencies>
<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>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-suite</artifactId>
<version>1.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
2)准备工具
创建驱动对象,创建获取屏幕截图方法
package com.blogWebAutoTest.common;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
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;
import java.util.ArrayList;
import java.util.List;
public class AutotestUtils {
public static ChromeDriver driver;
//创建驱动对象
public static ChromeDriver createDriver(){
//驱动对象已经创建好了/没有创建
if( driver == null){
ChromeOptions options = new ChromeOptions();
options.addArguments("--remote-allow-origins=*");
driver = new ChromeDriver(options);
//创建隐式等待
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
}
return driver;
}
public List<String> getTime(){
//文件能不能按照天的维度按文件夹进行保存
//文件格式 20230212-123030毫秒
SimpleDateFormat sim1 = new SimpleDateFormat("yyyyMMdd-HHmmssSS");
SimpleDateFormat sim2 = new SimpleDateFormat("yyyyMMdd");
String filename = sim1.format(System.currentTimeMillis());
String dirname = sim2.format(System.currentTimeMillis());
List<String> list = new ArrayList<>();
list.add(dirname);
list.add(filename);
return list;
}
/**
* 获取屏幕截图,把所有的用例执行的结果保存下来
*/
public void getScreenShot(String str) throws IOException {
List<String> list = getTime();
//dir+filename
// ./指的是当前的项目路径下,也就是BlogAutoTest下
// ./src/test/java/com/blogWebAutoTest/dirname/filename
// ./src/test/java/com/blogWebAutoTest/20230212/logintest_20230212-123030毫秒.png
String filename = "./src/test/java/com/blogWebAutoTest/"+list.get(0)+"/"+str+"_"+list.get(1)+".png";
File srcfile = driver.getScreenshotAs(OutputType.FILE);
//把屏幕截图生成的文件放到指定的路径
FileUtils.copyFile(srcfile,new File(filename));
}
}
3.2、登录界面测试
package com.blogWebAutoTest.Tests;
import com.blogWebAutoTest.common.AutotestUtils;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.io.IOException;
import java.time.Duration;
/**
* 博客登录页
*/
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BlogLoginTest extends AutotestUtils {
public static ChromeDriver driver = createDriver();;
//如果要测试登录页面,以下所有的用例都有一个共同的步骤
//1、要有浏览器对象 2、访问登录页面的url
@BeforeAll
static void baseControl(){
driver.get("http://localhost:8080/login.html");
}
/**
检查登录页面打开是否正确
检查点:主页 写博客 元素是否存在
*/
@Test
@Order(1)
void loginPageLoadRight() throws IOException {
driver.findElement(By.cssSelector("body > div.nav > a:nth-child(4)"));
driver.findElement(By.cssSelector("body > div.nav > a:nth-child(5)"));
getScreenShot(getClass().getName());
}
/**
* 检查正常登录情况
*/
@ParameterizedTest
@CsvSource({"wcx,123456"})
@Order(3)
void loginSuc(String name , String passwd) throws InterruptedException, IOException {
//这三步只是登录的步骤结束了,能不能保证登录是成功的呢??
driver.findElement(By.cssSelector("#username")).clear();
driver.findElement(By.cssSelector("#password")).clear();
driver.findElement(By.cssSelector("#username")).sendKeys(name);
driver.findElement(By.cssSelector("#password")).sendKeys(passwd);
driver.findElement(By.cssSelector("#submit")).click();
//对登录结果进行检测,如果跳转到了博客列表页才算是登录成功了
driver.findElement(By.cssSelector("#artDiv > div:nth-child(1) > a:nth-child(4)"));
getScreenShot(getClass().getName());
// driver.navigate().back();
}
/**
* 检查异常登录情况
*/
@ParameterizedTest
@CsvSource({"wcxx,123456","wcx,1234567",})
@Order(2)
void loginFail(String name, String passwd) throws IOException, InterruptedException {
driver.findElement(By.cssSelector("#username")).clear();
driver.findElement(By.cssSelector("#password")).clear();
driver.findElement(By.cssSelector("#username")).sendKeys(name);
driver.findElement(By.cssSelector("#password")).sendKeys(passwd);
driver.findElement(By.cssSelector("#submit")).click();
Thread.sleep(1000);
driver.switchTo().alert().accept();
driver.findElement(By.cssSelector("#submit"));
// driver.navigate().back();
}
}
3.3、个人博客列表页测试
package com.blogWebAutoTest.Tests;
import com.blogWebAutoTest.common.AutotestUtils;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeDriver;
import java.io.IOException;
/**
* 个人博客列表页
*/
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BlogListTest extends AutotestUtils {
public static ChromeDriver driver = createDriver();
@BeforeAll
static void baseControl(){
driver.get("http://localhost:8080/myblog_list.html");
}
/**
* 博客列表页可以正常显示
*/
@Test
void listPageLoadRight() throws IOException {
driver.findElement(By.cssSelector("#username"));
int num = driver.findElements(By.cssSelector("#artDiv > div.blog > div.title")).size();
System.out.println(num);
String articalNum = driver.findElement(By.cssSelector("#artCount")).getText();
Assertions.assertEquals(articalNum,num + "");
getScreenShot(getClass().getName());
}
}
3.4、编辑博客页测试
package com.blogWebAutoTest.Tests;
import com.blogWebAutoTest.common.AutotestUtils;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.provider.MethodSource;
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeDriver;
import java.io.IOException;
/**
* 博客编辑页
*/
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BlogEditTest extends AutotestUtils {
public static ChromeDriver driver = createDriver();
public static String num = null;
// @BeforeAll
// static void baseControl(){
// driver.get("http:localhost/blog_system/blog_edit.html");
// }
/**
* 检查博客编辑页可以正常打开
*/
@Test
@Order(1)
void editPageLoadRight() throws IOException {
num = driver.findElement(By.cssSelector("#artCount")).getText();
driver.findElement(By.cssSelector("body > div.nav > a:nth-child(5)")).click();
getScreenShot(getClass().getName());
}
@Test
@Order(2)
void editAndSubimitBlog() throws IOException, InterruptedException {
String expect = "java Autotest";
driver.findElement(By.cssSelector("#title")).sendKeys(expect);
//因博客系统使用到的编辑是第三方软件,所以不能直接使用sendKeys向编辑模块发送文本
driver.findElement(By.cssSelector("body > div.blog-edit-container > div.title > button")).click();
// getScreenShot(getClass().getName());
Thread.sleep(1000);
driver.switchTo().alert().accept();
Thread.sleep(1000);
driver.switchTo().alert().dismiss();
String actual = driver.findElement(By.cssSelector("#artCount")).getText();
System.out.println(num);
int a = Integer.parseInt(num)+1;
String numa = String.valueOf(a);
Assertions.assertEquals(numa,actual);
}
@Test
@Order(3)
void editNoTitle() throws InterruptedException {
driver.findElement(By.cssSelector("body > div.nav > a:nth-child(5)")).click();
driver.findElement(By.cssSelector("body > div.blog-edit-container > div.title > button")).click();
Thread.sleep(1000);
driver.switchTo().alert().accept();
Thread.sleep(1000);
driver.switchTo().alert().accept();
driver.navigate().back();
}
}
3.5、博客详情页测试
package com.blogWebAutoTest.Tests;
import com.blogWebAutoTest.common.AutotestUtils;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeDriver;
import java.io.IOException;
/**
* 博客详情页
*/
public class BlogDetailTest extends AutotestUtils {
public static ChromeDriver driver = createDriver();
public static String read = null;
@BeforeAll
static void baseControl(){
driver.get("http://localhost:8080/blog_list.html");
}
@Test
void blogDeailLoadRight() throws IOException{
driver.findElement(By.cssSelector("#artListDiv > div:nth-child(1) > a")).click();
String read = driver.findElement(By.cssSelector("#rcount")).getText();
driver.navigate().back();
driver.findElement(By.cssSelector("#artListDiv > div:nth-child(1) > a")).click();
String readAgain = driver.findElement(By.cssSelector("#rcount")).getText();
int a = Integer.parseInt(read)+1;
read=String.valueOf(a);
Assertions.assertEquals(read,readAgain);
String url = driver.getCurrentUrl();
Assertions.assertEquals("http://localhost:8080/blog_content.html?id=1",url);
getScreenShot(getClass().getName());
driver.navigate().back();
}
}
3.6、注册页面测试
package com.blogWebAutoTest.Tests;
import com.blogWebAutoTest.common.AutotestUtils;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeDriver;
/**
* 博客注册页
*/
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BlogRegisterTest extends AutotestUtils {
public static ChromeDriver driver = createDriver();
@BeforeAll
public static void baseControl(){
driver.get("http://localhost:8080/reg.html");
}
/**
* 检查正常注册情况
* @param name
* @param passwd
* @param repasswd
* @throws InterruptedException
*/
@ParameterizedTest
@CsvSource({"999,999,999"})
@Order(1)
public void regSuc(String name,String passwd,String repasswd) throws InterruptedException {
driver.findElement(By.cssSelector("#username")).clear();
driver.findElement(By.cssSelector("#password")).clear();
driver.findElement(By.cssSelector("#password2")).clear();
driver.findElement(By.cssSelector("#username")).sendKeys(name);
driver.findElement(By.cssSelector("#password")).sendKeys(passwd);
driver.findElement(By.cssSelector("#password2")).sendKeys(repasswd);
driver.findElement(By.cssSelector("#submit")).click();
Thread.sleep(1000);
String text = driver.switchTo().alert().getText();
// Assertions.assertEquals("恭喜:注册成功!是否要跳转到登录页面?",text);
driver.switchTo().alert().accept();
}
/**
* 检查异常注册情况(密码与确认密码不符)
* @param name
* @param passwd
* @param repasswd
* @throws InterruptedException
*/
@ParameterizedTest
@CsvSource({"999,999,998"})
@Order(2)
public void regFailWrongpassword(String name,String passwd,String repasswd) throws InterruptedException {
driver.findElement(By.cssSelector("#username")).clear();
driver.findElement(By.cssSelector("#password")).clear();
driver.findElement(By.cssSelector("#password2")).clear();
driver.findElement(By.cssSelector("#username")).sendKeys(name);
driver.findElement(By.cssSelector("#password")).sendKeys(passwd);
driver.findElement(By.cssSelector("#password2")).sendKeys(repasswd);
driver.findElement(By.cssSelector("#submit")).click();
Thread.sleep(1000);
String text = driver.switchTo().alert().getText();
// Assertions.assertEquals("两次密码输入的不一致,请先检查!",text);
driver.switchTo().alert().accept();
}
/**
* 检查异常注册情况(无用户名)
* @param passwd
* @param repasswd
* @throws InterruptedException
*/
@ParameterizedTest
@CsvSource({"999,998"})
@Order(3)
public void regFailname(String passwd,String repasswd) throws InterruptedException {
driver.findElement(By.cssSelector("#password")).clear();
driver.findElement(By.cssSelector("#password2")).clear();
driver.findElement(By.cssSelector("#password")).sendKeys(passwd);
driver.findElement(By.cssSelector("#password2")).sendKeys(repasswd);
driver.findElement(By.cssSelector("#submit")).click();
Thread.sleep(1000);
String text = driver.switchTo().alert().getText();
// Assertions.assertEquals("请先输入用户名!",text);
driver.switchTo().alert().accept();
}
/**
* 检查异常注册情况(无密码)
* @param name
* @param repasswd
* @throws InterruptedException
*/
@ParameterizedTest
@CsvSource({"999,999,998"})
@Order(4)
public void regFailpasswd(String name,String repasswd) throws InterruptedException {
driver.findElement(By.cssSelector("#username")).clear();
driver.findElement(By.cssSelector("#password2")).clear();
driver.findElement(By.cssSelector("#username")).sendKeys(name);
driver.findElement(By.cssSelector("#password2")).sendKeys(repasswd);
driver.findElement(By.cssSelector("#submit")).click();
Thread.sleep(1000);
String text = driver.switchTo().alert().getText();
// Assertions.assertEquals("请先输入密码!",text);
driver.switchTo().alert().accept();
}
/**
* 检查异常注册情况(无确认密码)
* @param name
* @param passwd
* @throws InterruptedException
*/
@ParameterizedTest
@CsvSource({"999,999,998"})
@Order(5)
public void regFailrepasswd(String name,String passwd) throws InterruptedException {
driver.findElement(By.cssSelector("#username")).clear();
driver.findElement(By.cssSelector("#password")).clear();
driver.findElement(By.cssSelector("#username")).sendKeys(name);
driver.findElement(By.cssSelector("#password")).sendKeys(passwd);
driver.findElement(By.cssSelector("#submit")).click();
Thread.sleep(1000);
String text = driver.switchTo().alert().getText();
// Assertions.assertEquals("请先输入确认密码!",text);
driver.switchTo().alert().accept();
}
}
运行效果