目录
- 一 项目介绍
- **项目名称**
- **项目介绍**
- **项目功能**
- **项目展示**
- 二 测试用例设计和功能测试
- 1.测试用例设计
- **①登录页面**
- **②注册页面**
- **③首页**
- **④发布帖子页面**
- **⑤修改个人信息页面**
- 2.功能测试环境
- 3.实际执行功能测试的部分操作
- **①登录页面**
- **②注册页面**
- **③首页**
- **④发布帖子页面**
- **⑤修改个人信息页面**
- 三 接口测试
- 1.Junit单元测试
- **①用户接口**
- **②帖子接口**
- 2.Swagger API测试
- **①用户接口**
- **②帖子接口**
- **③回复接口**
- **④版块接口**
- **⑤站内信接口**
- 四 自动化测试
- 1.使用selenium进行自动化测试准备工作
- 1.引入依赖
- 2.创建公共类
- 3.创建测试套件类
- 2.对登录页面进行自动化测试
- **①打开登陆网页**
- **②对登陆页面上的文字进行判断**
- **③测试窗口伸缩**
- **④错误的登录测试**
- **⑤正确的登录测试**
- **⑥运行结果**
- 3.对注册页面进行自动化测试
- **①打开注册页面**
- **②对注册页面上的文字进行判断**
- **④错误的注册测试**
- **⑤正确的注册测试**
一 项目介绍
项目名称
项目名称: code论坛
项目介绍
项目介绍: code论坛是一个基于Spring的前后端分离的在线论坛系统。使用了MySQL数据库来存储相关信息,项目完成后使用Xshell将其部署到云服务器上。
前端页面: 前端共由八个页面构成:注册页面,登录页面,首页,编辑帖子页面,帖子列表页面,个人中心页面,修改个人信息页面,帖子详情页面。
项目总结: 该项目可以实现通过发布帖子分享技术,并通过别人发布的帖子学习知识来进行技术交流,所以也可称之为技术交流社区。
项目功能
code论坛主要实现了以下功能:
用户注册,用户登录,编辑帖子,删除帖子,查看帖子,点赞帖子,站内信,编辑个人信息等功能。
项目展示
项目展示链接: code论坛
项目源码链接: code论坛源码
二 测试用例设计和功能测试
1.测试用例设计
测试用例会从界面测试,功能测试,性能测试,易用性测试,安全性测试,兼容性测试六个方面进行设计。
①登录页面
②注册页面
③首页
④发布帖子页面
⑤修改个人信息页面
2.功能测试环境
测试环境: win11
项目运行: CentOS,maven,JDK1.8
浏览器: FireFox浏览器,Chrome浏览器
3.实际执行功能测试的部分操作
①登录页面
页面展示
给定一个正确的账号密码:
用户名:小纸
密码:111
操作
输入用户名 | 输入密码 | 操作 | 预期结果 | 实际结果 |
---|---|---|---|---|
空 | 空 | 点击登录 | 提示用户名不能为空,密码不能为空 | 提示用户名不能为空,密码不能为空 |
张三(错误的用户名) | 空 | 点击登录 | 提示用户名或密码错误 | 提示用户名或密码错误 |
小纸(正确的用户名) | 空 | 点击登录 | 提示密码不能为空 | 提示密码不能为空 |
小纸(正确的用户名) | 123(错误的密码) | 点击登录 | 提示用户名或密码错误 | 提示用户名或密码错误 |
小纸(正确的用户名) | 111(正确的密码) | 点击登录 | 登录成功 | 成功登录 |
②注册页面
页面展示
操作
输入用户名 | 输入昵称 | 输入密码 | 输入确认密码 | 勾选同意条款 | 点击注册 | 预期结果 | 实际结果 |
---|---|---|---|---|---|---|---|
空 | 空 | 空 | 空 | 不勾选 | 点击注册 | 提示都不能为空 | 每个输入框下面都提示不能为空 |
张三 | 空 | 空 | 空 | 不勾选 | 点击注册 | 提示除用户名外都不能为空 | 其余三个输入框下面提示不能为空 |
张三 | 张三 | 111 | 123 | 勾选 | 点击注册 | 提示密码和确认密码不相同 | 提示请检查确认密码 |
张三 | 张三 | 111 | 111 | 不勾选 | 点击注册 | 提示请勾选 | 勾选框标红,点击注册按钮无结果 |
张三 | 张三 | 111 | 111 | 勾选 | 点击注册 | 注册成功 | 注册成功,跳转到登录页面,弹出是否要保存密码框 |
③首页
页面展示
操作
操作 | 预期结果 | 实际结果 |
---|---|---|
点击Java | 跳转至Java版块 | 跳转至Java版块 |
点击发布帖子 | 跳转至发布帖子页面 | 跳转至发布帖子页面 |
点击“计算机秋招经验” | 跳转至帖子详情页 | 跳转至帖子详情页 |
点击月亮标志 | 切换为夜晚模式 | 切换为夜晚模式 |
④发布帖子页面
页面展示
操作
输入标题 | 输入内容 | 操作 | 预期结果 | 实际结果 |
---|---|---|---|---|
空 | 空 | 点击发布 | 提示请输入帖子标题 | 提示请输入帖子标题 |
测试标题 | 空 | 点击发布 | 提示请输入帖子内容 | 提示请输入帖子内容 |
测试标题 | 测试内容 | 点击发布 | 发布成功 | 发布成功,跳转至首页 |
⑤修改个人信息页面
页面展示
操作
操作 | 预期结果 | 实际结果 |
---|---|---|
点击修改头像,上传头像 | 头像变为刚刚上传的图片 | 图片无变化(上传图片功能还未实现) |
输入邮箱地址,点击修改 | 修改成功 | 修改成功 |
输入电话号码,点击修改 | 修改成功 | 修改成功 |
输入错误原密码,点击提交修改 | 提示密码校验失败 | 提示密码校验失败 |
输入正确原密码,点击提交修改 | 修改成功 | 修改成功 |
三 接口测试
接口测试使用了:
- Junit单元测试
- Springfox Swagger 生成 API,完成 API 单元测试
1.Junit单元测试
单元测试类:
这里只列举用户接口和帖子接口的单元测试,其余的欢迎参考我的gitee链接:
code论坛
①用户接口
这里列举了注册,登录和修改个人信息的测试代码。
注册方法测试:
@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("qqq@qq.com");// 邮箱
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("删除成功");
}
2.Swagger API测试
测试链接:code论坛系统API
接口测试版块总览:
回复接口:
帖子接口:
版块接口:
用户接口:
站内信接口:
⭐⭐⭐这里每个接口展示一个功能的测试过程。
①用户接口
功能:用户登录
账号:小纸
密码:111
过程:
结果:登录成功
②帖子接口
功能:获取用户列子列表
输入用户id:1
过程:
结果:获取成功,用户id为1的用户共发布一篇文章
③回复接口
功能:发布回复
输入发布回复的帖子id:19
输入回复内容:支持好文!!!
过程:
结果:回复成功
④版块接口
功能:获取首页版块列表
过程:直接点击Execute
结果:操作成功
⑤站内信接口
功能:发送站内信
输入接收用户id:2
输入内容:你好
过程:
结果:操作成功
四 自动化测试
1.使用selenium进行自动化测试准备工作
1.引入依赖
创建一个maven项目,在pop.xml中引入以下依赖
<dependencies>
<!-- https://mvnrepository.com/artifact/org.seleniumhq.selenium/selenium-java -->
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.2</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.2</version>
</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>
<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>
</dependencies>
2.创建公共类
因为对每一个页面进行测试都需要创建浏览器驱动,所以我们可以把他提取出来并设置成静态的,就可以让创建和销毁驱动的操作只实现一次,其他类都继承这个类即可。
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;
}
}
3.创建测试套件类
创建一个类,通过
@Suite
注解识别该类为测试套件类,使用@SelectClasses
来注解声明我们要运行哪些类。
package org.example.tests;
import org.junit.platform.suite.api.SelectClasses;
import org.junit.platform.suite.api.Suite;
/**
* 测试套件类
*/
@Suite
@SelectClasses({loginTest.class, IndexTest.class})
public class RunSuite {
}
2.对登录页面进行自动化测试
①打开登陆网页
②对登陆页面上的文字进行判断
- 页面上的用户名,密码,登录按钮是否都正常显示
- 点击注册按钮能否正常跳转页面
③测试窗口伸缩
测试窗口缩小至指定大小,放大到最大
④错误的登录测试
- 用户名为空,提示用户名不能为空
- 密码为空,提示密码不能为空
- 用户名密码不匹配,提示用户名或密码错误
⑤正确的登录测试
输入正确的用户名与密码,登陆成功,跳转至首页,判断跳转的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();// 这里不需要返回,并未跳转页面
}
}
3.对注册页面进行自动化测试
①打开注册页面
②对注册页面上的文字进行判断
- 判断用户注册,用户名,昵称,密码,确认密码,同意文字是否存在
- 注册按钮是否存在
- 登陆的超链接是否存在
④错误的注册测试
- 用户名,昵称,密码,确认密码,同意隐私政策,其中有任何一个不填写,注册失败
⑤正确的注册测试
- 用户名,昵称,密码,确认密码,同意隐私政策,所有都正确填写,注册成功
- 注册成功后,跳转到登陆界面