项目展示链接: 学习交流社区
项目介绍: 学习交流社区是一个基于Spring的前后端分离的在线论坛系统。使用了MySQL数据库来存储相关信息,项目完成后使用Xshell将其部署到云服务器上。
前端页面: 前端共由八个页面构成:注册页面,登录页面,首页,编辑帖子页面,帖子列表页面,个人中心页面,修改个人信息页面,帖子详情页面。
项目总结: 该项目可以实现通过发布帖子分享技术,并通过别人发布的帖子学习知识来进行技术交流,所以也可称之为技术交流社区。
学习交流社区主要实现了以下功能:
用户注册,用户登录,编辑帖子,删除帖子,查看帖子,点赞帖子,站内信,编辑个人信息等功能。
测试用例会从界面测试,功能测试,性能测试,易用性测试,安全性测试,兼容性测试六个方面进行设计。
测试环境: win11
项目运行: CentOS,maven,JDK1.8
浏览器: Chrome浏览器
给定一个正确的账号密码:
用户名:锦鲤
密码:123456
操作
输入用户名 | 输入密码 | 操作 | 预期结果 | 实际结果 |
---|---|---|---|---|
空 | 空 | 点击登录 | 提示用户名不能为空,密码不能为空 | 提示用户名不能为空,密码不能为空 |
张三(错误的用户名) | 空 | 点击登录 | 提示用户名或密码错误 | 提示用户名或密码错误 |
锦鲤(正确的用户名) | 空 | 点击登录 | 提示密码不能为空 | 提示密码不能为空 |
锦鲤(正确的用户名) | 123(错误的密码) | 点击登录 | 提示用户名或密码错误 | 提示用户名或密码错误 |
锦鲤(正确的用户名) | 123456(正确的密码) | 点击登录 | 登录成功 | 成功登录 |
页面展示
操作
输入用户名 | 输入昵称 | 输入密码 | 输入确认密码 | 勾选同意条款 | 点击注册 | 预期结果 | 实际结果 |
---|---|---|---|---|---|---|---|
空 | 空 | 空 | 空 | 不勾选 | 点击注册 | 提示都不能为空 | 每个输入框下面都提示不能为空 |
张三 | 空 | 空 | 空 | 不勾选 | 点击注册 | 提示除用户名外都不能为空 | 其余三个输入框下面提示不能为空 |
张三 | 张三 | 111 | 123 | 勾选 | 点击注册 | 提示密码和确认密码不相同 | 提示请检查确认密码 |
张三 | 张三 | 111 | 111 | 不勾选 | 点击注册 | 提示请勾选 | 勾选框标红,点击注册按钮无结果 |
张三 | 张三 | 111 | 111 | 勾选 | 点击注册 | 注册成功 | 注册成功,跳转到登录页面,弹出是否要保存密码框 |
页面展示
操作 | 预期结果 | 实际结果 |
---|---|---|
点击Java | 跳转至Java版块 | 跳转至Java版块 |
点击发布帖子 | 跳转至发布帖子页面 | 跳转至发布帖子页面 |
点击“任一帖子标题” | 跳转至帖子详情页 | 跳转至帖子详情页 |
点击月亮标志 | 切换为夜晚模式 | 切换为夜晚模式 |
点击铃铛标志 | 显示所有私信 | 显示所有私信 |
操作
输入标题 | 输入内容 | 操作 | 预期结果 | 实际结果 |
---|---|---|---|---|
空 | 空 | 点击发布 | 提示请输入帖子标题 | 提示请输入帖子标题 |
测试标题 | 空 | 点击发布 | 提示请输入帖子内容 | 提示请输入帖子内容 |
测试标题 | 测试内容 | 点击发布 | 发布成功 | 发布成功,跳转至首页 |
操作
操作 | 预期结果 | 实际结果 |
---|---|---|
点击修改头像,上传头像 | 头像变为刚刚上传的图片 | 图片无变化(上传图片功能还未实现) |
输入邮箱地址,点击修改 | 修改成功 | 修改成功 |
输入电话号码,点击修改 | 修改成功 | 修改成功 |
输入错误原密码,点击提交修改 | 提示密码校验失败 | 提示密码校验失败 |
输入正确原密码,点击提交修改 | 修改成功 | 修改成功 |
接口测试使用了:
- Junit单元测试
- Springfox Swagger 生成 API,完成 API 单元测试
单元测试类:
这里只列举用户接口和帖子接口的单元测试,其余类似
这里列举了注册,登录和修改个人信息的测试代码。
注册方法测试:
@Test
@Transactional
void createNormalUser() {
// 构造User对象
User user = new User();
user.setUsername("boy1");
user.setNickname("boy");
// 定义一个原始的密码
String password = "123456";
// 生成盐
String salt = UUIDUtil.UUID_32();
// 生成密码的密文
String ciphertext = MD5Util.md5Salt(password, salt);
// 设置加密后的密码
user.setPassword(ciphertext);
// 设置盐
user.setSalt(salt);
// 调用Service层的方法
userService.createNormalUser(user);
// 打印结果
System.out.println(user);
}
修改个人信息测试:
@Test
@Transactional
void modifyInfo() {
User user = new User();
user.setId(3l); // 用户Id
user.setUsername("testUser"); // 登录名
user.setNickname("testUser1"); // 昵称
user.setGender(null); // 性别
user.setEmail("[email protected]");// 邮箱
user.setPhoneNum("15366668888"); // 电话
user.setRemark("测试"); // 个人简介
// 调用Service
userService.modifyInfo(user);
}
登录测试:
@Test
void login() {
User user = userService.login("bitboy", "123456");
System.out.println(user);
}
这里列举了发布帖子,查询所有帖子列表,删除帖子,点赞帖子的测试代码。
发布帖子测试:
@Test
@Transactional
void create() {
Article article = new Article();
article.setUserId(2L); // boy
article.setBoardId(1L); // java版块
article.setTitle("单元测试");
article.setContent("测试内容");
articleService.create(article);
System.out.println("发贴成功");
}
查询所有帖子列表:
@Test
void selectAll() throws JsonProcessingException {
// 调用Service
List<Article> articles = articleService.selectAll();
// 转换成JSON字符串并且打印
System.out.println(objectMapper.writeValueAsString(articles));
}
点赞帖子和删除帖子方法测试:
@Test
@Transactional
void thumbsUpById() {
articleService.thumbsUpById(1L);
System.out.println("点赞成功");
}
@Test
@Transactional
void deleteById() {
articleService.deleteById(11l);
System.out.println("删除成功");
}
测试链接:学习交流社区
接口测试版块总览:
回复接口:
帖子接口:
版块接口:
用户接口:
站内信接口:
⭐⭐⭐这里每个接口展示一个功能的测试过程。
功能:用户登录
账号:锦鲤
密码:123456
功能:获取用户列子列表
输入用户id:1
测试过程:
结果:获取成功,用户id为1的用户共发布一篇文章
功能:发布回复
输入发布回复的帖子id:19
输入回复内容:支持好文!!!
过程:
结果:回复成功
功能:获取首页版块列表
过程:直接点击Execute
结果:操作成功
功能:发送站内信
输入接收用户id:2
输入内容:你好
过程:
结果:操作成功
创建一个maven项目,在pop.xml中引入以下依赖
<dependencies>
<dependency>
<groupId>org.seleniumhq.seleniumgroupId>
<artifactId>selenium-javaartifactId>
<version>3.141.59version>
dependency>
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.11.0version>
dependency>
<dependency>
<groupId>org.junit.jupitergroupId>
<artifactId>junit-jupiter-apiartifactId>
<version>5.9.2version>
dependency>
<dependency>
<groupId>org.junit.jupitergroupId>
<artifactId>junit-jupiter-paramsartifactId>
<version>5.9.2version>
dependency>
<dependency>
<groupId>org.junit.platformgroupId>
<artifactId>junit-platform-suiteartifactId>
<version>1.9.1version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.junit.platformgroupId>
<artifactId>junit-platform-suiteartifactId>
<version>1.9.1version>
dependency>
<dependency>
<groupId>org.junit.jupitergroupId>
<artifactId>junit-jupiter-engineartifactId>
<version>5.9.1version>
<scope>testscope>
dependency>
dependencies>
因为对每一个页面进行测试都需要创建浏览器驱动,所以我们可以把他提取出来并设置成静态的,就可以让创建和销毁驱动的操作只实现一次,其他类都继承这个类即可
package org.example.common;
import org.openqa.selenium.chrome.ChromeDriver;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
public class AutotestUtils {
private static ChromeDriver driver;
public static ChromeDriver createDrive() {
if (driver == null) {
driver = new ChromeDriver();
// 隐式等待, 渲染页面, 防止找不到页面元素
driver.manage().timeouts().implicitlyWait(10,TimeUnit.SECONDS);
}
return driver;
}
}
创建一个类,通过@Suite注解识别该类为测试套件类,使用@SelectClasses来注解声明我们要运行哪些类
package org.example.tests;
import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;
/**
* 2023/8/21
* 测试套件类
*/
@Suite
@SelectClasses({loginTest.class, IndexTest.class})
public class RunSuite {
}
测试窗口缩小至指定大小,放大到最大
输入正确的用户名与密码,登陆成功,跳转至首页,判断跳转的url是否正确,以及跳转页面上的文字是否显示正确
测试全部通过
※这里给出一个页面的详细代码,其他页面都与之类似
package org.example.tests;
import org.example.common.AutotestUtil;
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.Dimension;
import org.openqa.selenium.chrome.ChromeDriver;
import java.util.concurrent.TimeUnit;
/**
* wangcong
* 2023/8/21
* 登陆页面测试
*/
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class loginTest extends AutotestUtil {
// 获取浏览器驱动
public static ChromeDriver driver = createDrive();
/**
* 打开网页
*/
@Test
@BeforeAll
static void init() {
// 跳转到登录页面
driver.get("http://47.109.128.149:6060/sign-in.html");
// 隐式等待页面加载完成
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
}
/**
* 对登陆页面的一些文字显示进行判断
*/
@Test
@Order(1)
void loginPageTest() {
// 检查系统名称
String expect = "用户登录";
String actual = driver.findElement(By.cssSelector("body > div > div > div > div:nth-child(1) > div > div.card.card-md > div > h2")).getText();
Assertions.assertEquals(expect, actual);
// 判断登录窗口的内容
String expect2 = driver.findElement(By.cssSelector("#signInForm > div.mb-3 > label")).getText();
Assertions.assertEquals(expect2, "用户名");
String expect3 = driver.findElement(By.cssSelector("#signInForm > div.mb-2 > label")).getText();
Assertions.assertEquals(expect3, "密码");
// 检查登陆按钮是否存在
driver.findElement(By.cssSelector("#submit"));
// 检查注册按钮是否存在
driver.findElement(By.cssSelector("body > div > div > div > div:nth-child(1) > div > div.text-center.text-muted.mt-3 > a"));
// 点击注册按钮
driver.findElement(By.cssSelector("body > div > div > div > div:nth-child(1) > div > div.text-center.text-muted.mt-3 > a")).click();
// 判断跳转页面是否正确
String url = driver.getCurrentUrl();
Assertions.assertEquals(url, "http://47.109.128.149:6060/sign-up.html");
}
/**
* 测试窗口伸缩
*/
@Test
@Order(2)
public void windowSize() {
driver.manage().window().setSize(new Dimension(900, 900));
driver.manage().window().setSize(new Dimension(300, 300));
driver.manage().window().maximize();
}
/**
* 错误登录测试1
*/
@ParameterizedTest
@Order(4)
@CsvSource(value = {"锦鲤, 12345"})
void loginAbnormal1(String username, String password) {
// 清空用户名和密码
driver.findElement(By.cssSelector("#username")).clear();
driver.findElement(By.cssSelector("#password")).clear();
// 用户名为空
driver.findElement(By.cssSelector("#username")).click();
driver.findElement(By.cssSelector("#password")).sendKeys(password);
String expect = driver.findElement(By.cssSelector("#signInForm > div.mb-3 > div")).getText();
// 断言
Assertions.assertEquals(expect, "用户名不能为空");
// 密码为空
driver.findElement(By.cssSelector("#username")).sendKeys(username);
driver.findElement(By.cssSelector("#password")).clear();
String expectPass = driver.findElement(By.cssSelector("#signInForm > div.mb-2 > div > div")).getText();
// 断言
Assertions.assertEquals(expectPass, "密码不能为空");
// 返回上一个页面
// driver.navigate().back();// 这里不需要返回,并未跳转页面
}
/**
* 错误登录测试2
*/
@ParameterizedTest
@Order(4)
@CsvSource(value = {"锦鲤, 12345","锦鲤呀,123456"})
void loginAbnormal2(String username, String password) {
// 清空用户名和密码
driver.findElement(By.cssSelector("#username")).clear();
driver.findElement(By.cssSelector("#password")).clear();
// 输入用户名和密码
driver.findElement(By.cssSelector("#username")).sendKeys(username);
driver.findElement(By.cssSelector("#password")).sendKeys(password);
// 点击登录
driver.findElement(By.cssSelector("#submit")).click();
// 等待弹窗内容
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
// 判断弹窗内容
String expect = driver.findElement(By.cssSelector("body > div.jq-toast-wrap.bottom-right > div > h2")).getText();
// 断言
Assertions.assertEquals(expect, "警告");
// 返回上一个页面
// driver.navigate().back();// 这里不需要返回,并未跳转页面
}
/**
* 正确登录测试
*/
@ParameterizedTest
@Order(4)
@CsvSource(value = {"小金, 123456","锦鲤, 123456"})
void loginNormal(String username, String password) {
// 清空用户名和密码
driver.findElement(By.cssSelector("#username")).clear();
driver.findElement(By.cssSelector("#password")).clear();
// 输入用户名和密码
driver.findElement(By.cssSelector("#username")).sendKeys(username);
driver.findElement(By.cssSelector("#password")).sendKeys(password);
// 点击登录
driver.findElement(By.cssSelector("#submit")).click();
// 等待跳转页面
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
// 判断跳转页面url
String url = driver.getCurrentUrl();
// 断言url
Assertions.assertEquals(url, "http://47.109.128.149:6060/sign-in.html");
// 判断显示是否为首页
String expect = driver.findElement(By.cssSelector("#article_list_board_title")).getText();
// 断言
Assertions.assertEquals(expect, "首页");
// 返回上一个页面
driver.navigate().back();// 这里不需要返回,并未跳转页面
}
}