实现一个网页版的博客系统,提供了一个技术文章论坛,同时也提供了用户之间在线交流的平台。
网页博客系统支持以下核心功能:
online 好友聊天室支持以下核心功能:
额外扩展功能:
1. 文章评论功能.
2. 文章点赞功能.
3. 文章保存草稿功能.
4. 用户找回密码功能.
5. 用户相互关注功能,附带粉丝列表,关注列表.
6. 用户个⼈中心:支持修改密码、修改昵称(非登录名)、修改密保等功能.
7. 使用多线程(使用线程池优化)实现文章定时发布功能.
8. 使用多线程(使用线程池优化)实现用户多次登录,账号冻结的业务.
9. 使用 MultipartFile 实现上传头像,使用 WebMvcConfigurer 配置自定义资源映射实现头像展示.
10. 手动实现密码加盐算法,提高用户信息安全性.
11. 使用 hutool 工具,实现了登录时图片验证码功能,增加了系统的安全性.
12. 使用 HandlerInterceptor 实现了统一登录拦截器.
13. 使用 ResponseBodyAdvice 实现了统一数据格式返回.
整个 Web 项目分为以下页面
客户端输入用户名、密码、验证码,后端首先检验验证码受否正确,然后通过 Spring + MyBatis 进行数据库的查询操作校验用户的账号和秘密是否正确,若正确则将 Session 信息持久化到 Redis 上并跳转到博客列表页,若错误则用弹窗进行提示。
客户端输入账号和密码,增加用户,通过 MySQL 将用户信息进行持久化保存.
服务器通过 Spring + MyBatis 进行信息的增添.
客户端输入找回账号、真实密码、身份证号作为修改密码的前置步骤,前置步骤验证通过后,服务器通过 Spring + MyBatis 进行数据库的修改密码.
面向任意用户,博客列表支持分页功能,可以在该界面访问所有用发布的博客以及访问量点赞信息,并且可以点击 “查看全文” 查看博客详细信息.
服务器通过 Spring + MyBatis 进行数据库的查询操作,查询所有文章.
博客详情页可以,查看博客的详细信息以及作者信息,已登录用户可以进行关注,发表评论,删除评论.
服务器通过 Spring + MyBatis 进行数据库的查询操作,查询当前访问的文章和作者信息.
面向当前登录用户. 该页面用户可以填写标题和正文,最后点击发布按钮进行发布博客; 用户也可以选择定时发布博客,让博客在指定时间发布; 用户也可以选择先不发布博客,保存草稿.
服务器通过 MySQL 保存用户发布的博客信息.
面向当前登录用户. 该页面可以进行对之前发布的页面进行编辑操作,修改完后点击发布博客即可修改文章; 用户也可以选择定时发布博客,让博客在指定时间发布; 用户也可以选择先不发布博客,保存草稿.
服务器通过 Spring + MyBatis 对数据库进行相应的修改操作。
面向当前登录用户. 该页面可以对之前保存的文章草稿进行修改和删除.
服务器针对用户的以上操通过 Spring + MyBatis 进行相应的增删改查操作.
面向当前登录用户. 可以对当前登录用户进行信息修改.
服务器针对用户的以上操通过 Spring + MyBatis 进行相应的增删改查操作.
面向当前登录用户. 该页面会展示用户的个人发布的所有博客,若是自己的主页可以对这些博客进行查看详情,修改,删除等功能,若是他人主页可以查看文章详情,并且可以查看粉丝列表和关注列表.
服务器针对用户的以上操通过 Spring + MyBatis 进行相应的增删改查操作.
面向已登录的用户. 该页面通过消息推送机制,展现在线与离线好友(相互关注),可以与好友进行聊天,消息通过 WebSocket 进行实时推送.
a)输入异常的账号、密码、验证码
预期结果:弹窗对应提示
实际结果如下:
b)输入正常的账号和密码
预期结果:跳转到用户主页
实际结果如下:
a)输入异常的账号、密码、确认密码
预期结果:对应的弹窗提示
实际结果如下:
b)输入正常的账号、密码、确认密码
预期结果:弹窗提示是否跳转到登录页面
a)异常填写账号、密保信息、密码
预期结果:对应的弹窗提示
实际结果如下:
b)输入正确的信息
预期结果:弹窗提示密码找回成功,是否跳转到登录页.
实际结果如下:
b)分页功能测试
预期结果:上一页,下一页,首页,末页均有边界处理
实际结果如下:
a)预期结果:点击点赞按钮,标识已点赞,再点一次取消点赞
实际结果如下:
c)预期结果:点击删除评论,进行信息删除,若不是自己的评论(作者除外),无法删除
实际结果如下:
a)预期结果:点击删除草稿,进行弹窗提示,若确认,则删除草稿。
实际结果如下:
a)预期结果:点击发布博客,进行对应弹窗提示,确认后,回到主页即可看到博客 .
实际结果如下:
b)预期结果:点击定时发布功能,设置时间为1分钟后发布(若没设置时间,则进行弹窗提示),并在1分钟后在主页观察到博客。
实际结果如下:
c)预期结果:点击保存草稿后,在草稿列表显示
实际结果如下:
a)预期结果:登录后相互关注的好友可以观察到对方上线消息.
实际结果如下:
b)预期结果:双方互发消息,消息气泡在对应一侧,并且消息及时推送
c)预期结果:输入框输入消息,点击发送按钮后,清空消息框
实际结果如下:
单例驱动: 自动化程序中会很频繁的使用驱动,如果频繁的创建和销毁开销还是比较大的,因此我们可以使用一种懒汉模式的加载方式去加载驱动,这样既能保证驱动不会频繁创建(程序运行过程保持单例),又能减轻程序刚开始启动时的系统开销(只有用到驱动的时候才去加载他),其他类如果需要驱动直接继承该类即可.
屏幕截图: 有的时候我们测试用例执行出错了,我们需要查看当时网页出现的情况,那么就需要使用屏幕截图来排查问题.
具体的,可以使用驱动的 getScreenshotAs 方法去保存屏幕截图,在每一个测试case执行完后进行一次屏幕截图,并将截图保存到一个路径下,文件名以当时时间进行组织(防止保存屏幕截图出现覆盖情况),那么就可以在AutoTestUtils类下加上屏幕截图的方法,方便其他类调用.
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.edge.EdgeOptions;
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 {
private static EdgeDriver driver;
public static EdgeDriver getDriver() {
EdgeOptions options = new EdgeOptions();
options.addArguments("--remote-allow-origins=*");
if(driver == null) {
driver = new EdgeDriver(options);
//创建隐式等待,设置最长等待时间为 10s
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
}
return driver;
}
/**
* 获取屏幕截图,将用例执行结果保存下来
* @param str
*/
public void getScreenShot(String str) throws IOException {
List<String> list = getTime();
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));
}
//获取当前时间(记录每个屏幕截图是什么时候拍摄的)
private List<String> getTime() {
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMMdd-HHmmssSS");
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyyMMdd");
//保存的文件夹名:当天的年月 + 具体时间
String fileName = sdf1.format(System.currentTimeMillis());
//保存的图片名:当天年月
String dirName = sdf2.format(System.currentTimeMillis());
List<String> list = new ArrayList<>();
list.add(dirName);
list.add(fileName);
return list;
}
}
创建一个类(自定义名为 RegTest)继承 AutoTestUtils,得到驱动,首先测试登录页面是否正常打开,接着找几个典型的 case 使用参数化注解进行测试注册功能即可,最后进行相应弹窗处理,最后进行屏幕截图.
import com.common.AutoTestUtils;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.edge.EdgeDriver;
import java.io.IOException;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class RegTest extends AutoTestUtils {
//获取到驱动
private static EdgeDriver driver = AutoTestUtils.getDriver();
/**
* 打开网页
*/
@BeforeAll
public static void openWeb() {
driver.get("http://43.139.193.116:8080/reg.html");
}
/**
* 验证网页正常打开
*/
@Test
@Order(1)
public void webAppear() throws IOException {
driver.findElement(By.cssSelector("#username"));
driver.findElement(By.cssSelector("body > div.login-container > div > div:nth-child(4)"));
driver.findElement(By.xpath("//*[@id=\"submit\"]"));
driver.findElement(By.xpath("/html/body/div[2]/div"));
//屏幕截图
getScreenShot(getClass().getName());
}
/**
* 异常测试注册功能
* @param username
* @param password
*/
@ParameterizedTest
@CsvSource({"zhangsan, 123, 123", "aaa, 123, 1233"})
@Order(2)
public void regFunTest(String username, String password, String confirmPassword) throws InterruptedException, IOException {
//拿到元素
WebElement inputUsername = driver.findElement(By.cssSelector("#username"));
WebElement inputPassword = driver.findElement(By.cssSelector("#password"));
WebElement inputconfirmPassword = driver.findElement(By.cssSelector("#password2"));
WebElement button = driver.findElement(By.cssSelector("#submit"));
//先清除输入框
inputUsername.clear();
inputPassword.clear();
inputconfirmPassword.clear();
//输入用例
inputUsername.sendKeys(username);
inputPassword.sendKeys(password);
inputconfirmPassword.sendKeys(confirmPassword);
//提交
button.click();
//处理弹窗
Thread.sleep(100);
driver.switchTo().alert().accept();
//屏幕截图
getScreenShot(getClass().getName());
}
}
创建一个类(自定义名为 LoginTest)继承 AutoTestUtils 类,获取驱动,之后选取典型 case 使用参数化注解对异常、正常登录分别进行测试,进行相应弹窗处理,最后进行屏幕截图.
import com.common.AutoTestUtils;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.edge.EdgeDriver;
import java.io.IOException;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class LoginTest extends AutoTestUtils {
//获取驱动
private static EdgeDriver driver = AutoTestUtils.getDriver();
/**
* 打开网页
* @BeforeAll 标识在所有测试用例执行之前执行依次。
*/
@BeforeAll
private static void openWeb() {
driver.get("http://43.139.193.116:8080/login.html");
}
/**
* 检测登录页面能否正常打开
*/
@Test
@Order(1)
public void elementsAppear() throws IOException {
driver.findElement(By.cssSelector("#username"));
driver.findElement(By.cssSelector("#checkpassword-img"));
driver.findElement(By.cssSelector("body > div.login-container > div"));
driver.findElement(By.cssSelector("body > div.login-container > div > div.assist > a:nth-child(2)"));
//屏幕截图
getScreenShot(getClass().getName());
}
/**
* 检测登录异常测试
* @param username
* @param password
* @param checkPassword 验证码
* Ps: 万能验证码为 chenyikang
*/
@ParameterizedTest
@CsvSource({"adm* , 1234, chenyikang", "daf, 11, chenyikang", "&*^% , 123, aaa"})
@Order(2)
public void loginAbnormalTest(String username, String password, String checkPassword) throws InterruptedException, IOException {
//清除用户名、密码框、验证码
driver.findElement(By.cssSelector("#username")).clear();
driver.findElement(By.cssSelector("#password")).clear();
driver.findElement(By.cssSelector("#checkpassword")).clear();
//输入账号、密码、验证码
driver.findElement(By.cssSelector("#username")).sendKeys(username);
driver.findElement(By.cssSelector("#password")).sendKeys(password);
driver.findElement(By.cssSelector("#checkpassword")).sendKeys(checkPassword);
driver.findElement(By.cssSelector("#submit")).click();
//强制等待,等待弹窗
Thread.sleep(100);
//处理弹窗
Alert alert = driver.switchTo().alert();
//期望结果
String expect = "用户名/密码/验证码错误";
String actual = "用户名/密码/验证码错误";
//实际结果(若未出现弹窗,说明不符合预期);
if(alert == null) {
actual = "当前页面异常";
}
alert.accept();
//屏幕截图
getScreenShot(getClass().getName());
Assertions.assertEquals(expect, actual);
}
/**
* 正常登录测试
* @param username
* @param password
* @throws InterruptedException
*/
@ParameterizedTest
@CsvSource({"zhangsan, 123"})
@Order(3)
public void loginNormalTest(String username, String password) throws InterruptedException, IOException {
//清除用户名、密码框、验证码
driver.findElement(By.cssSelector("#username")).clear();
driver.findElement(By.cssSelector("#password")).clear();
driver.findElement(By.cssSelector("#checkpassword")).clear();
//输入账号、密码、验证码
driver.findElement(By.cssSelector("#username")).sendKeys(username);
driver.findElement(By.cssSelector("#password")).sendKeys(password);
driver.findElement(By.cssSelector("#checkpassword")).sendKeys("chenyikang");
driver.findElement(By.cssSelector("#submit")).click();
//屏幕截图
getScreenShot(getClass().getName());
//登录成功需要返回登录界面
//点击注销
driver.findElement(By.cssSelector("body > div.nav > a:nth-child(9)")).click();
//处理弹窗
//强制等待,等待弹窗
Thread.sleep(100);
driver.switchTo().alert().accept();
//等待处理完成
Thread.sleep(100);
}
}
创建一个类(自定义名为 fundPasswordTest )继承 AutoTestUtils 类,获取驱动,首先验证页面是否能正常显示,接着找几个典型的 case 使用参数化注解对其进行测试,进行相应弹窗处理,最后进行屏幕截图.
import com.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.edge.EdgeDriver;
import java.io.IOException;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class fundPasswordTest extends AutoTestUtils {
//获取驱动
private static EdgeDriver driver = AutoTestUtils.getDriver();
/**
* 打开页面
*/
@BeforeAll
public static void openWeb() {
driver.get("http://43.139.193.116:8080/fund_password.html");
}
/**
* 验证页面正常打开
*/
@Test
@Order(1)
public void WebAppear() throws IOException {
driver.findElement(By.cssSelector("#ps"));
driver.findElement(By.cssSelector("#realname"));
driver.findElement(By.cssSelector("#submit"));
driver.findElement(By.xpath("/html/body/div[2]/div"));
//屏幕截图
getScreenShot(getClass().getName());
}
/**
* 异常找回密码测试
* @param username
* @param realname
* @param id
* @param newPassword
* @param password
*/
@ParameterizedTest
@CsvSource({"wu, zhangsan, 610, 123, 123", "zhangsan, lisi, 610, 123, 123",
"zhangsan, zhangsan, 611, 123, 123", "zhangsan, zhangsan, 610, 123, 12234"})
@Order(2)
public void fundPasswordFunExceptionTest(String username, String realname,
String id, String newPassword, String password) throws InterruptedException, IOException {
//先清除输入框
driver.findElement(By.cssSelector("#username")).clear();
driver.findElement(By.cssSelector("#realname")).clear();
driver.findElement(By.cssSelector("#idcard")).clear();
driver.findElement(By.cssSelector("#password1")).clear();
driver.findElement(By.cssSelector("#password2")).clear();
//输入
driver.findElement(By.cssSelector("#username")).sendKeys(username);
driver.findElement(By.cssSelector("#realname")).sendKeys(realname);
driver.findElement(By.cssSelector("#idcard")).sendKeys(id);
driver.findElement(By.cssSelector("#password1")).sendKeys(newPassword);
driver.findElement(By.cssSelector("#password2")).sendKeys(password);
//提交
driver.findElement(By.cssSelector("#submit")).click();
//处理弹窗
Thread.sleep(100);
driver.switchTo().alert().accept();
//屏幕截图
getScreenShot(getClass().getName());
//处理完弹窗若还能发现页面元素,说明异常处理成功
String expect = "找回密码";
String actual = driver.findElement(By.cssSelector("body > div.login-container > div > h3")).getText();
Assertions.assertEquals(expect, actual);
}
/**
* 正常找回密码
* @param username
* @param realname
* @param id
* @param newPassword
* @param password
*/
@ParameterizedTest
@CsvSource("zhangsan, zhangsan, 610, 123, 123")
@Order(3)
public void fundPasswordFunNormalTest(String username, String realname,
String id, String newPassword, String password) throws InterruptedException, IOException {
//先清除输入框
driver.findElement(By.cssSelector("#username")).clear();
driver.findElement(By.cssSelector("#realname")).clear();
driver.findElement(By.cssSelector("#idcard")).clear();
driver.findElement(By.cssSelector("#password1")).clear();
driver.findElement(By.cssSelector("#password2")).clear();
//输入
driver.findElement(By.cssSelector("#username")).sendKeys(username);
driver.findElement(By.cssSelector("#realname")).sendKeys(realname);
driver.findElement(By.cssSelector("#idcard")).sendKeys(id);
driver.findElement(By.cssSelector("#password1")).sendKeys(newPassword);
driver.findElement(By.cssSelector("#password2")).sendKeys(password);
//提交
driver.findElement(By.cssSelector("#submit")).click();
//处理弹窗
Thread.sleep(100);
driver.switchTo().alert().accept();
//屏幕截图
getScreenShot(getClass().getName());
Thread.sleep(100);
String expect = "登陆";
String actual = driver.findElement(By.cssSelector("body > div.login-container > div > h3")).getText();
Assertions.assertEquals(expect, actual);
}
}
创建一个类(自定义名为 PersonalInfoTest)继承 AutoTestUtils,得到驱动,对基础信息、密保信息、修改密码使用典型 case 进行测试,进行相应弹窗处理,最后进行屏幕截图.
import com.common.AutoTestUtils;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.edge.EdgeDriver;
import java.io.IOException;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class PersonalInfoTest extends AutoTestUtils {
private static EdgeDriver driver = AutoTestUtils.getDriver();
@BeforeAll
public static void openWeb() throws InterruptedException {
// //方式一:单元测试
// driver.get("http://43.139.193.116:8080/login.html");
// driver.findElement(By.cssSelector("#username")).clear();
// driver.findElement(By.cssSelector("#password")).clear();
// driver.findElement(By.cssSelector("#checkpassword")).clear();
// //输入账号、密码、验证码
// driver.findElement(By.cssSelector("#username")).sendKeys(AppValues.USERNAME);
// driver.findElement(By.cssSelector("#password")).sendKeys(AppValues.PASSWORD);
// driver.findElement(By.cssSelector("#checkpassword")).sendKeys(AppValues.CODE);
// driver.findElement(By.cssSelector("#submit")).click();
// Thread.sleep(100);
// driver.findElement(By.cssSelector("body > div.nav > a:nth-child(5)")).click();
//整体测试
//此时已经有 session 会话了,直接进入即可
driver.get("http://43.139.193.116:8080/myinfo.html");
}
/**
* 验证页面正常打开
*/
@Test
@Order(1)
public void webAppear() throws IOException {
driver.findElement(By.cssSelector("#photo"));
driver.findElement(By.cssSelector("#gitee"));
driver.findElement(By.cssSelector("#submit"));
driver.findElement(By.cssSelector("body > div.login-container > div > div:nth-child(12)"));
//屏幕截图
getScreenShot(getClass().getName());
}
/**
* 修改基础信息测试
* @param nickname
*/
@ParameterizedTest
@ValueSource(strings = {"123", "xiaozhang", "&*^%$", "小张"})
@Order(2)
public void basicInfoTest(String nickname) throws InterruptedException, IOException {
//清除输入框
driver.findElement(By.cssSelector("#nickname")).clear();
//输入信息
driver.findElement(By.cssSelector("#nickname")).sendKeys(nickname);
//提交
driver.findElement(By.xpath("/html/body/div[2]/div/div[3]/button")).click();
//处理弹窗
Thread.sleep(100);
driver.switchTo().alert().accept();
//屏幕截图
getScreenShot(getClass().getName());
}
/**
* 修改密保信息测试
*/
@ParameterizedTest
@CsvSource({"132, &*^, 610", "$$#@^7., &$(#)+, 610", "123, zhangsan, 610"})
@Order(3)
public void SecurityInfoTest(String username, String realname, String id) throws InterruptedException, IOException {
//清除输入框
driver.findElement(By.cssSelector("#password")).clear();
driver.findElement(By.cssSelector("#realname")).clear();
driver.findElement(By.cssSelector("#idcard")).clear();
//输入信息
driver.findElement(By.cssSelector("#password")).sendKeys(username);
driver.findElement(By.cssSelector("#realname")).sendKeys(realname);
driver.findElement(By.cssSelector("#idcard")).sendKeys(id);
//提交
driver.findElement(By.xpath("/html/body/div[2]/div/div[6]/button")).click();
//处理弹窗
Thread.sleep(200);
driver.switchTo().alert().accept();
//屏幕截图
getScreenShot(getClass().getName());
//提交
driver.findElement(By.xpath("/html/body/div[2]/div/div[7]/button")).click();
//处理弹窗
Thread.sleep(200);
driver.switchTo().alert().accept();
}
/**
* 异常密码测试1
* @param oldPassword
* @param newPassword
* @param confirmPassword
*/
@ParameterizedTest
@CsvSource({"134358, 123, 123", "*#&2*, 123, 123"})
@Order(4)
public void updatePasswordTest1(String oldPassword, String newPassword, String confirmPassword) throws InterruptedException, IOException {
//清除输入框
driver.findElement(By.cssSelector("#password1")).clear();
driver.findElement(By.cssSelector("#password2")).clear();
driver.findElement(By.cssSelector("#password3")).clear();
//输入
driver.findElement(By.cssSelector("#password1")).sendKeys(oldPassword);
driver.findElement(By.cssSelector("#password2")).sendKeys(newPassword);
driver.findElement(By.cssSelector("#password3")).sendKeys(confirmPassword);
//提交
driver.findElement(By.xpath("/html/body/div[2]/div/div[12]/button")).click();
//处理弹窗
Thread.sleep(100);
Alert alert = driver.switchTo().alert();
String e1 = "旧密码错误,请重新输入!";
String a1 = alert.getText();
alert.accept();
//屏幕截图
getScreenShot(getClass().getName());
//断言
Assertions.assertEquals(e1, a1);
}
/**
* 异常密码测试2
* @param oldPassword
* @param newPassword
* @param confirmPassword
*/
@ParameterizedTest
@CsvSource({"123, adfa, 1551sf8", "123, 489, 123"})
@Order(5)
public void updatePasswordTest(String oldPassword, String newPassword, String confirmPassword) throws InterruptedException, IOException {
//清除输入框
driver.findElement(By.cssSelector("#password1")).clear();
driver.findElement(By.cssSelector("#password2")).clear();
driver.findElement(By.cssSelector("#password3")).clear();
//输入
driver.findElement(By.cssSelector("#password1")).sendKeys(oldPassword);
driver.findElement(By.cssSelector("#password2")).sendKeys(newPassword);
driver.findElement(By.cssSelector("#password3")).sendKeys(confirmPassword);
//提交
driver.findElement(By.xpath("/html/body/div[2]/div/div[12]/button")).click();
//处理弹窗
Thread.sleep(100);
Alert alert = driver.switchTo().alert();
String e1 = "新密码与确认密码不一致,请重试!";
String a1 = alert.getText();
alert.accept();
//屏幕截图
getScreenShot(getClass().getName());
//断言
Assertions.assertEquals(e1, a1);
}
/**
* 正常修改密码测试
* @param oldPassword
* @param newPassword
* @param confirmPassword
*/
@ParameterizedTest
@CsvSource({"123, 1234, 1234", "1234, 123, 123"})
@Order(5)
public void updatePasswordNorTest(String oldPassword, String newPassword, String confirmPassword) throws InterruptedException, IOException {
//清除输入框
driver.findElement(By.cssSelector("#password1")).clear();
driver.findElement(By.cssSelector("#password2")).clear();
driver.findElement(By.cssSelector("#password3")).clear();
//输入
driver.findElement(By.cssSelector("#password1")).sendKeys(oldPassword);
driver.findElement(By.cssSelector("#password2")).sendKeys(newPassword);
driver.findElement(By.cssSelector("#password3")).sendKeys(confirmPassword);
//提交
driver.findElement(By.xpath("/html/body/div[2]/div/div[12]/button")).click();
//处理弹窗
Thread.sleep(100);
Alert alert = driver.switchTo().alert();
String e1 = "密码修改成功!";
String a1 = alert.getText();
alert.accept();
//屏幕截图
getScreenShot(getClass().getName());
//断言
Assertions.assertEquals(e1, a1);
}
}
创建一个类名为 PersonalHomeTest 继承 AutoTestUtils ,得到驱动,分别对博客列表信息、粉丝列表、关注列表进行检验,最后对关注功能进行检验,进行相应弹窗处理,最后进行屏幕截图.
import com.common.AppValues;
import com.common.AutoTestUtils;
import org.junit.jupiter.api.*;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.edge.EdgeDriver;
import java.io.IOException;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class PersonalHomeTest extends AutoTestUtils {
//获取驱动
private static EdgeDriver driver = AutoTestUtils.getDriver();
/**
* 进入到个人主页中
* Ps: 这里不能直接通过网址进入,需要先登录
*/
@BeforeAll
private static void intoWeb() {
// //先检验拦截
// driver.get("http://43.139.193.116:8080/myblog_list.html?id=2");
// //检验拦截后是否跳转到登录页面
// String expect = "登陆";
// String actual = driver.findElement(By.cssSelector("body > div.login-container > div > h3")).getText();
// Assertions.assertEquals(expect, actual);
//登录
driver.get("http://43.139.193.116:8080/login.html");
driver.findElement(By.cssSelector("#username")).clear();
driver.findElement(By.cssSelector("#password")).clear();
driver.findElement(By.cssSelector("#checkpassword")).clear();
//输入账号、密码、验证码
driver.findElement(By.cssSelector("#username")).sendKeys(AppValues.USERNAME);
driver.findElement(By.cssSelector("#password")).sendKeys(AppValues.PASSWORD);
driver.findElement(By.cssSelector("#checkpassword")).sendKeys(AppValues.CODE);
driver.findElement(By.cssSelector("#submit")).click();
}
/**
* 检验网页是否正常打开
*/
@Test
@Order(1)
public void webAppear() throws IOException {
driver.findElement(By.cssSelector("#photo"));
driver.findElement(By.cssSelector("body > div.container > div.container-left > div > div:nth-child(5) > div"));
driver.findElement(By.cssSelector("body > div.container > div.container-right > div.selectInfo > span:nth-child(3)"));
driver.findElement(By.cssSelector("#listInfo"));
//屏幕截图
getScreenShot(getClass().getName());
}
/**
* 辅助栏检验
* Ps:顺带检验关注功能
*/
@Test
@Order(2)
public void assistColumnTest() throws InterruptedException, IOException {
//1.进入关注列表
driver.findElement(By.cssSelector("body > div.container > div.container-right > div.selectInfo > span:nth-child(3)")).click();
//找到关注元素
WebElement followButton = driver.findElement(By.cssSelector("#follow-msg"));
//检验关注和取关
String e1 = "已关注";
String a1 = followButton.getText();
Assertions.assertEquals(e1, a1);
//进行取关
followButton.click();
//等待服务器响应一会
Thread.sleep(100);
//屏幕截图
getScreenShot(getClass().getName());
//2.进入粉丝列表
driver.findElement(By.cssSelector("body > div.container > div.container-right > div.selectInfo > span:nth-child(2)")).click();
//进行关注
driver.findElement(By.cssSelector("#follow-msg")).click();
//检验是否关注成功
String e2 = "已关注";
Thread.sleep(500);
String a2 = driver.findElement(By.cssSelector("#follow-msg")).getText();
//屏幕截图
getScreenShot(getClass().getName());
Assertions.assertEquals(e2, a2);
}
}
创建一个类名为 DraftListTest 继承 AutoTestUtils 类,获取驱动,在对草稿的编辑,删除功能分别进行测试,进行相应弹窗处理,最后进行屏幕截图.
import com.common.AutoTestUtils;
import org.junit.jupiter.api.*;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.edge.EdgeDriver;
import java.io.IOException;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class DraftListTest extends AutoTestUtils {
//获取驱动
private static EdgeDriver driver = AutoTestUtils.getDriver();
/**
* 进入到个人草稿页中
*/
@BeforeAll
public static void intoWeb() {
// //方式一:单元测试
// driver.get("http://43.139.193.116:8080/login.html");
// driver.findElement(By.cssSelector("#username")).clear();
// driver.findElement(By.cssSelector("#password")).clear();
// driver.findElement(By.cssSelector("#checkpassword")).clear();
// //输入账号、密码、验证码
// driver.findElement(By.cssSelector("#username")).sendKeys(AppValues.USERNAME);
// driver.findElement(By.cssSelector("#password")).sendKeys(AppValues.PASSWORD);
// driver.findElement(By.cssSelector("#checkpassword")).sendKeys(AppValues.CODE);
// driver.findElement(By.cssSelector("#submit")).click();
// driver.findElement(By.cssSelector("body > div.nav > a:nth-child(7)")).click();
//整体测试
//此时已经有 session 会话了,直接进入即可
driver.get("http://43.139.193.116:8080/draft_list.html?id=2");
}
/**
* 检查当前页面元素出现
*/
@Test
@Order(1)
public void webAppear() throws IOException {
driver.findElement(By.cssSelector("#photo"));
driver.findElement(By.cssSelector("body > div.container > div.container-left > div > div:nth-child(4) > div"));
driver.findElement(By.cssSelector("#listInfo"));
driver.findElement(By.cssSelector("#listInfo > div > a > div.title"));
//屏幕截图
getScreenShot(getClass().getName());
}
/**
* 检验草稿功能
*/
@Test
@Order(2)
public void draftTest() throws InterruptedException, IOException {
//1.检验草稿是否能打开
driver.findElement(By.cssSelector("#listInfo > div > a > div.title")).click();
String e1 = "这是一篇草稿";
String a1 = driver.findElement(By.cssSelector("#title")).getText();
Assertions.assertEquals(e1, a1);
//后退
driver.navigate().back();
//2.检验修改草稿按钮
Assertions.assertNotNull(driver.findElement(By.cssSelector("#edit")));
//3.检验删除草稿功能
driver.findElement(By.cssSelector("#del")).click();
Alert alert = driver.switchTo().alert();
Thread.sleep(100);
Assertions.assertEquals("确定删除该文章?", alert.getText());
//处理弹窗(取消)
alert.dismiss();
//屏幕截图
getScreenShot(getClass().getName());
}
}
创建一个类名为 BlogAndDraftPostTest 继承 AutoTestUtils 类,得到驱动,并对发布按钮和保存按钮进行自动化测试,进行相应弹窗处理,最后进行屏幕截图.
import com.common.AutoTestUtils;
import org.junit.jupiter.api.*;
import org.openqa.selenium.By;
import org.openqa.selenium.edge.EdgeDriver;
import java.io.IOException;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class BlogAndDraftPostTest extends AutoTestUtils {
//获取驱动
private static EdgeDriver driver = AutoTestUtils.getDriver();
/**
* 打开当前页面
*/
@BeforeAll
public static void openWeb() {
// //方式一:单元测试
// driver.get("http://43.139.193.116:8080/login.html");
// driver.findElement(By.cssSelector("#username")).clear();
// driver.findElement(By.cssSelector("#password")).clear();
// driver.findElement(By.cssSelector("#checkpassword")).clear();
// //输入账号、密码、验证码
// driver.findElement(By.cssSelector("#username")).sendKeys(AppValues.USERNAME);
// driver.findElement(By.cssSelector("#password")).sendKeys(AppValues.PASSWORD);
// driver.findElement(By.cssSelector("#checkpassword")).sendKeys(AppValues.CODE);
// driver.findElement(By.cssSelector("#submit")).click();
// driver.findElement(By.cssSelector("body > div.nav > a:nth-child(6)")).click();
//整体测试
//此时已经有 session 会话了,直接进入即可
driver.get("http://43.139.193.116:8080/blog_add.html");
}
/**
* 检验当前页面
*/
@Test
@Order(1)
public void webAppear() throws IOException {
driver.findElement(By.cssSelector("#title"));
driver.findElement(By.cssSelector("#pubdate"));
driver.findElement(By.cssSelector("#editorDiv > div.CodeMirror.cm-s-default.CodeMirror-wrap > div.CodeMirror-scroll"));
driver.findElement(By.cssSelector("body > div.blog-edit-container > div.title > button:nth-child(4)"));
//屏幕截图
getScreenShot(getClass().getName());
}
/**
* 测试博客发布功能
*/
@Test
@Order(2)
public void postFun() throws InterruptedException, IOException {
//输入标题和正文
driver.findElement(By.cssSelector("#title")).sendKeys("test");
driver.findElement(By.cssSelector("#editorDiv > div.editormd-toolbar > div > ul > li:nth-child(7) > a")).click();
//点击发布博客
driver.findElement(By.cssSelector("body > div.blog-edit-container > div.title > button:nth-child(4)")).click();
Thread.sleep(100);
driver.switchTo().alert().accept();
Thread.sleep(100);
driver.switchTo().alert().dismiss();
//检查是否成功发布
//屏幕截图
getScreenShot(getClass().getName());
String e1 = "test";
String a1 = driver.findElement(By.cssSelector("#listInfo > div:nth-child(1) > a > div.title")).getText();
Assertions.assertEquals(e1, a1);
//删除博客
driver.findElement(By.xpath("/html/body/div[2]/div[2]/div[2]/div[1]/div/a[3]")).click();
Thread.sleep(100);
driver.switchTo().alert().accept();
//屏幕截图
getScreenShot(getClass().getName());
Thread.sleep(200);
driver.switchTo().alert().accept();
}
/**
* 检验保存草稿功能
*/
@Test
@Order(3)
public void saveBlog() throws InterruptedException, IOException {
//进入博客发布页面
driver.findElement(By.cssSelector("body > div.nav > a:nth-child(6)")).click();
//输入标题和正文
driver.findElement(By.cssSelector("#title")).sendKeys("test");
driver.findElement(By.cssSelector("#editorDiv > div.editormd-toolbar > div > ul > li:nth-child(7) > a")).click();
//点击保存草稿
driver.findElement(By.cssSelector("body > div.blog-edit-container > div.title > button:nth-child(5)")).click();
//处理弹窗
Thread.sleep(100);
driver.switchTo().alert().accept();
//屏幕截图
getScreenShot(getClass().getName());
//进入草稿页面
driver.findElement(By.cssSelector("body > div.nav > a:nth-child(7)")).click();
//检验是否保存成功
String e1 = "test";
String a1 = driver.findElement(By.cssSelector("#listInfo > div:nth-child(1) > a > div.title")).getText();
Assertions.assertEquals(e1, a1);
//删除草稿
driver.findElement(By.xpath("/html/body/div[2]/div[2]/div[1]/div/a[3]")).click();
//处理弹窗
Thread.sleep(100);
driver.switchTo().alert().accept();
//屏幕截图
getScreenShot(getClass().getName());
Thread.sleep(100);
driver.switchTo().alert().accept();
}
}
创建一个类名为 OnlineTalkTest 继承 AutoTestUtils ,得到驱动,对在线显示以及聊天功能进行测试,最后进行相应弹窗处理,最后进行屏幕截图.
import com.common.AutoTestUtils;
import org.junit.jupiter.api.*;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.openqa.selenium.Alert;
import org.openqa.selenium.By;
import org.openqa.selenium.edge.EdgeDriver;
import java.io.IOException;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class OnlineTalkTest extends AutoTestUtils {
private static EdgeDriver driver = AutoTestUtils.getDriver();
@BeforeAll
public static void openWeb() throws InterruptedException {
// //方式一:单元测试
// driver.get("http://43.139.193.116:8080/login.html");
// driver.findElement(By.cssSelector("#username")).clear();
// driver.findElement(By.cssSelector("#password")).clear();
// driver.findElement(By.cssSelector("#checkpassword")).clear();
// //输入账号、密码、验证码
// driver.findElement(By.cssSelector("#username")).sendKeys(AppValues.USERNAME);
// driver.findElement(By.cssSelector("#password")).sendKeys(AppValues.PASSWORD);
// driver.findElement(By.cssSelector("#checkpassword")).sendKeys(AppValues.CODE);
// driver.findElement(By.cssSelector("#submit")).click();
// Thread.sleep(100);
// driver.findElement(By.cssSelector("body > div.nav > a:nth-child(8)")).click();
//整体测试
//此时已经有 session 会话了,直接进入即可
driver.get("http://43.139.193.116:8080/private_letter.html");
}
/**
* 测试页面正常打开
*/
@Test
@Order(1)
public void webAppear() throws IOException {
driver.findElement(By.cssSelector("body > div.container > div.container-left > div.mycard > img"));
driver.findElement(By.cssSelector("body > div.container > div.container-left > div.mycard > span"));
driver.findElement(By.cssSelector("body > div.container > div.container-left"));
driver.findElement(By.cssSelector("body > div.container > div.container-right > div"));
//屏幕截图
getScreenShot(getClass().getName());
}
/**
* 测试聊天功能
*/
@ParameterizedTest
@ValueSource(strings = {"你好呀~", "#*&*@%)"})
@Order(2)
public void TalkTest(String msg) throws InterruptedException, IOException {
Thread.sleep(100);
//点击头像开始聊天
driver.findElement(By.xpath("/html/body/div[2]/div[1]/div[2]/div")).click();
//选择输入框开始聊天
driver.findElement(By.cssSelector("#messageText")).sendKeys(msg);
//提交
driver.findElement(By.cssSelector("#sendButton")).click();
//处理弹窗
Thread.sleep(100);
Alert alert = driver.switchTo().alert();
String e1 = "对方不在线!";
String a1 = alert.getText();
Thread.sleep(100);
alert.accept();
//屏幕截图
getScreenShot(getClass().getName());
//断言
Assertions.assertEquals(e1, a1);
}
}
在所有自动化测试用例执行完后,需要进行退出浏览器,那么我们可以创建一个退出驱动类,放在测试套件类的最后一个测试类。
import com.common.AutoTestUtils;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.edge.EdgeDriver;
/**
* 退出驱动类
*/
public class DriverQuitTest extends AutoTestUtils {
private static EdgeDriver driver = AutoTestUtils.getDriver();
/**
* 退出驱动
*/
@Test
public void quitWeb() {
driver.quit();
}
}
创建一个类(自定义名为RunSuite),通过 @Suite 注解标识该类为测试套件类(不是测试类),然后使用 @SelectClasses 注解来声明我们要指定的类(通过这个类来运行测试用例),这样优点如下:
1.相比于一个个函数调用来对测试用例进行测试就大大减少了开销和时间;
2.同时指定了类的测试顺序,即在注解@SelectClasses参数中的类测试顺序为从左向右;
import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;
@Suite
@SelectClasses({LoginTest.class, RegTest.class, fundPasswordTest.class,
PersonalHomeTest.class, DraftListTest.class, BlogAndDraftPostTest.class,
PersonalInfoTest.class, OnlineTalkTest.class, DriverQuitTest.class})
public class RunSuite {}
我们使用以上三个工具针对我们的项目进行性能测试。
a)Virtual User Generator(简称VUG): 用来生成性能测试脚本。
b)Controller: 创建测试场景,运行和监控场景。
c)Analysis: 生成测试报告,分析性能测试结果
a)访问博客登录页;
b)执行登录;
c)进入首页.
1、创建项目
由于我们测试的博客系统是一个Web项目,因此需要创建一个Web性能测试脚本,如下:
2.运行设置
代码编写(我们主要在Action里编写代码~)
主要的操作步骤为:1.打开网页,2.输入账号密码并登录;
在此期间,为了更好的让我们进行性能测试的数据收集,我们可以使用
Action()
{
//开启事务1
lr_start_transaction("index_trans");
//1.访问登录页面
web_url("web_url",
"URL=http://43.139.193.116:8080/blog_system/login.html",
"TargetFrame=",
"Resource=0",
"Referer=",
LAST);
//登录的集合点
lr_rendezvous("login_rendezvous");
//开始事务2
lr_start_transaction("login_trans");
//2.输入登录账号和密码
web_submit_form("web_submit_form",
ITEMDATA,
"Name=username", "Value={zhangsan}", ENDITEM,
"Name=password", "Value=123", ENDITEM,
LAST);
//结束事务2
lr_end_transaction("login_trans", LR_AUTO);
//结束事务1
lr_end_transaction("index_trans", LR_AUTO);
return 0;
}
a)针对我们已经编写好的脚本打开controller工具,创建测试脚本,如下:
在 controller 创建的场景中直接生成测试报告,如下:
作用:通过显示的虚拟用户数量可以判断出哪个时间段服务器负载最大(上图00:40 ~ 01:40负载最大)。
作用:通过点击率可以判断出某时间段内服务器的负载。
问题一:为什么吞吐量图和点击数图相似,但是吞吐量图要滞后一点?
问题二:如果请求变多但是吞吐量没变化,原因是什么?
作用: 可以观察到,虚拟用户在性能测试中,每秒在服务器上命中的次数,可以根据命中的次数评估虚拟用户生成的负载量。