學號:04191315
姓名:何翔
學院:計算機學院
專業:軟件工程
文章鏈接:https://blog.csdn.net/HXBest/article/details/122083502
完整代碼:https://github.com/He-Xiang-best/Software-Quality-Assurance-and-Testing
修訂歷史記錄
版本 | 日期 | AMD | 修訂者 | 說明 |
---|---|---|---|---|
V1.0 | 2021.12.7 | A | 何翔 | 完成了單元測試和使用Selenium+TestNG+Excel讀取數據並測試。功能比較簡單,不足之處是沒有使用到數據庫數據的讀取,以及測試失敗后提示不全面,也只是簡單的判空和頁面跳轉,沒有做斷言的處理,沒有報告分析。 |
V2.0 | 2021.12.21 | A、M | 何翔 | 在V1.0的基礎上添加了使用Selenium+TestNG+數據庫讀取數據並測試。代碼基本重構了一遍,好的地方是判斷測試用例情況更加全面,且增加了斷言處理,有生成代碼的測試報告等。不足之處是斷言的數據的使用不對,沒有增加預期值的字段,反而使用了一些不需要的數據作為是預期的判斷。 |
V2.0+ | 2021.12.28 | A、M | 何翔 | 代碼在V2.0的基礎上進行全面優化,增加了預期值的字段,實際值能夠在程序運行過程中獲取,與我們的數據庫給定的預期值做斷言比較,測試用例更加全面完善,且所有測試都成功通過,功能模塊沒有bug產生,測試報告等也全面完成,分析准確到位。 |
(A-添加,M-修改,D-刪除)
一、測試需求
1.1 測試模塊
后台登入功能模塊
后台指網站或系統用於管理用戶數據、網站或系統數據的一部分,一般只允許管理員或特定人員通過后台登錄界面進入,對整個網站及系統進行管理,普通用戶是沒有權限進入的。后台管理主要是用於對網站前台的信息管理,如文字、圖片、影音、和其他日常使用文件的發布、更新、刪除等操作,同時也包括各種子模塊信息的管理。由此可見,后台管理了一個系統相當多至關重要的數據,操控人員一定是需要安全檢驗認證才可訪問的,進而對數據進行管理,因此,設計一個安全嚴謹的后台的登入功能模塊是非常需要的。現對一個設計好的后台登入模塊進行登錄的測試。
1.2 測試內容
-
使用【Selenium+Java+數據庫】進行數據驅動測試,對自己搭建的Web項目做登入功能測試
-
使用【Selenium+Java+Excel】進行數據驅動測試,對自己搭建的Web項目做登入功能測試
-
使用【Junit】對自己開發的web程序進行單元測試,實現簡單的增刪查改操作
二、測試設計思想
2.1 測試用例
字段名稱 | 描 述 |
---|---|
標識符 | UC1 |
測試項 | 登入功能 |
設計者 | 何翔 |
測試環境要求 | 與服務器可以正常連接 ;軟件:Chrome瀏覽器96版本以上 ,jdk1.8+,maven相關依賴以及TestNG相關jar包 |
測試方法 | 黑盒測試 |
輸入說明 | (1) 訪問后台(2)填寫登入信息,其中所填寫的“用戶名”、“密碼”兩個輸入框不能為空,且登入的用戶信息需要和注冊保存在數據庫里面的數據一致(3)點擊登入按鈕 |
輸出標准 | 界面提示信息: (1)登入成功時有提示,並能夠跳轉成功的相關頁面(2)當輸入的信息不符合要求時,要有具體提示(3)登入失敗的時,顯示登入失敗具體失敗的具體原因。 |
特殊要求 | 進入到后台登入頁面 |
用例之間的依賴性 | 無 |
2.2 等價類划分
我們可以設用戶輸入的登入用戶名為:input_username,輸入的登入密碼為:input_password;正確對應存在的登入用戶名為:username,正確對應存在的登入密碼為:password。
一個用戶想要登入進后台管理系統,需要滿足以下條件:
-
登入用戶名輸入框已填寫數據:
input_username ≠ 空
-
登入密碼輸入框已填寫數據:
input_password ≠ 空
-
如果表單信息都填寫了,還要判斷填寫的用戶名存在:
input_username = username
-
如果用戶名存在,還要判斷填寫的密碼與存在用戶的密碼一致:
input_password = password
輸入條件 | 有效等價類編號 | 有效等價類 | 無效等價類編號 | 無效等價類 |
---|---|---|---|---|
是否填寫用戶名 | (1) | input_username ≠ 空 | (2) | input_username = 空 |
是否填寫密碼 | (3) | input_password ≠ 空 | (4) | input_password = 空 |
是否存在用戶 | (5) | input_username = username | (6) | input_username ≠ username |
是否密碼一致 | (7) | input_password = password | (8) | input_password ≠ password |
注:以下的XXX表示的是非正確的隨機數據
序號 | 輸入值(input_username/input_password) | 覆蓋等價類編號 | 輸出 |
---|---|---|---|
1 | (“”,XXX) | (2),(3),(6),(8) | 請輸入用戶名 |
2 | (“”,"") | (2),(4),(6),(8) | 請輸入用戶名 |
3 | (“”,password) | (2),(3),(6),(7) | 請輸入用戶名 |
4 | (XXX,“ ”) | (1),(4),(6),(8) | 請輸入密碼 |
5 | (username,“ ”) | (1),(4),(5),(8) | 請輸入密碼 |
6 | (XXX,XXX) | (1),(3),(6),(8) | 用戶不存在 |
7 | (XXX,password) | (1),(3),(6),(7) | 用戶不存在 |
8 | (username,XXX) | (1),(3),(5),(8) | 密碼輸入錯誤 |
9 | (username,password) | (1),(3),(5),(7) | 登入成功 |
三、測試數據
數據庫數據 | Excel數據 | 測試用例說明 |
---|---|---|
![]() |
![]() |
![]() |
測試用例編號 | 輸入數據 | 預期值 |
---|---|---|
case1 | input_username = 張三;input_password = 123 | 提示“用戶不存在” |
case2 | input_username = admin;input_password = admin | 提示“密碼輸入錯誤 ” |
case3 | input_username = 李四;input_password = error | 提示“用戶不存在” |
case4 | input_username = “”;input_password = 123 | 提示“請輸入用戶名” |
case5 | input_username = admin;input_password = “” | 提示“請輸入密碼” |
case6 | input_username = “”;input_password = “” | 提示“請輸入用戶名” |
case7 | input_username = 王五; input_password = “” | 提示“請輸入密碼” |
case8 | input_username = “”;input_password = error | 提示“請輸入用戶名” |
case9 | input_username = admin;input_password = 123 | 登入成功,進入后台主頁 |
四、測試代碼(核心部分)
4.1 TestNG 登入測試
4.1.1 TestNGConfig.java
package com.study.config;
/*
* @ClassName TestNGConfig
* @description: 測試代碼相關配置信息和數據文件信息
* @author: 何翔
* @Date 2021/12/27 18:49
*/
import com.alibaba.excel.EasyExcel;
import java.sql.*;
import java.util.List;
import java.util.Map;
public class TestNGConfig {
static final String url = "jdbc:mysql://localhost/db_springboot?&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true";
static final String driver = "com.mysql.cj.jdbc.Driver";
static final String sqlUserName ="root";
static final String sqlPassword = "1234";
static final String ExcelPath = "C:\\StudyProject\\Project\\Java\\SpringBoot\\springboot\\src\\test\\resources" +
"\\TestNGData.xlsx";
static String username ="admin";
static String password ="123";
static String expectResult="登錄成功";
public static String getIp(){return "http://localhost:8080/";}
public static Object[][] getDatabaseData() {
//讀取數據庫文件信息
Object[][] data = null;
try {
Class.forName(driver);
Connection con = DriverManager.getConnection(url,sqlUserName, sqlPassword);
Statement s = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
ResultSet rs = s.executeQuery("select * from user_copy ");
int total = 0;
while (rs.next()) {
total++;
}
data = new Object[total][3];
rs.beforeFirst();
int a = 0;
while (rs.next()) {
data[a][0] = rs.getString("user_name");
data[a][1] = rs.getString("user_pwd");
data[a][2] = rs.getString("expect_result");
a++;
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return data;
}
public static Object[][] getExcelData(){
//讀取Excel文件數據
List<Map<Integer, String>> list = EasyExcel
.read(ExcelPath)
.sheet()
.doReadSync();
Object[][] data = new Object[list.size()][];
int row=0, column;
for (Map<Integer, String> map : list) {
data[row] = new Object[map.size()];
column=0;
for (String value : map.values()) {
data[row][column] = value;
column++;
}
row++;
}
return data;
}
public static String getExpectResult() {return expectResult;}
public static void setExpectResult(String expectResult) {TestNGConfig.expectResult = expectResult;}
public static String getUsername() {return username;}
public static void setUsername(String username) {TestNGConfig.username = username;}
public static String getPassword() {return password;}
public static void setPassword(String password) {TestNGConfig.password = password;}
}
4.1.2 TestNGWebTest.java
package com.study.webTest;
/*
* @ClassName TestNGWebTest
* @description: 使用TestNG對數據庫信息或excel文件信息進行登入功能測試
* @author: 何翔
* @Date 2021/11/23 8:47
*/
import com.study.config.TestNGConfig;
import org.openqa.selenium.By;
import org.openqa.selenium.chrome.ChromeDriver;
import org.testng.annotations.*;
import static org.testng.AssertJUnit.assertEquals;
public class TestNGWebTest{
static ChromeDriver driver;
//在當前測試類開始時運行。
@BeforeClass
public void beforeClass(){
System.out.println("-------------------beforeClass");
System.setProperty("webdriver.chrome.driver", "src/test/resources/chromedriver.exe");
driver = new ChromeDriver();
}
//每個測試方法運行之前運行
@BeforeMethod
public void before(){
System.out.println("=====beforeMethod");
}
@Test(dataProvider="getDatabaseData")
public void webTestByUseDatabase(String username , String password, String result){testContent(username, password,result);}
@DataProvider(name = "getDatabaseData")
public Object[][] getDatabaseData() {returnTestNGConfig.getDatabaseData();}
@Test(dataProvider="getExcelData")
public void webTestByUseExcel(String username , String password, String result){testContent(username, password,result);}
@DataProvider(name = "getExcelData")
public Object[][] getExcelData() {
return TestNGConfig.getExcelData();
}
//每個測試方法運行之后運行
@AfterMethod
public void after(){
System.out.println("=====afterMethod");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//在當前測試類結束時運行。
@AfterClass
public static void afterClass(){
driver.quit();
System.out.println("-------------------afterClass");
}
public void testContent(String username , String password , String result){
System.out.println("=====testMethod");
driver.get(TestNGConfig.getIp());
driver.findElement(By.name("username")).sendKeys(username);
driver.findElement(By.name("password")).sendKeys(password);
driver.findElement(By.id("sign-in-submit")).click();
assertEquals( driver.findElement(By.id("expect_result")).getAttribute("value"), result);
}
}
4.2 Junit 單元測試
package com.study;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.study.springboot.entity.User;
import com.study.springboot.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.util.List;
@SpringBootTest
class SpringbootApplicationTests {
/*
* @author: 何翔
* @date: 2021/10/6 0:56
* @description:security 安全訪問測試
*/
@Test
public void contextLords(){
PasswordEncoder pe = new BCryptPasswordEncoder();
String encode = pe.encode("123");
System.out.println(encode);
boolean matches = pe.matches("123",encode);
System.out.println(matches);
}
/*
* @author: 何翔
* @date: 2021/10/6 0:55
* @description:mybatis-plus 數據庫測試
*/
@Autowired
UserService userService;
@Test
public void query() {
//System.out.println(userService.getById(1));
System.out.println(userService.list(null));
}
@Test
void insert() {
User user = new User();
user.setUserName("李四");
user.setUserPwd("456");
System.out.println(userService.save(user));
System.out.println(user.getUserId());
}
@Test
void delete() {
System.out.println(userService.removeById(11));
}
@Test
void update() {
System.out.println(userService.update(new UpdateWrapper<User>().lambda()
.set(User::getUserPwd, "223").eq(User::getUserId, 11)));
}
@Test
void page() {
IPage<User> iPage = new Page<>(1,2);
IPage<User> page = userService.page(iPage);
List<User> records = page.getRecords();
System.out.println(records);
System.out.println(page.getPages());
}
}
五、數據分析
5.1 測試運行分析
![]() |
![]() |
![]() |
---|---|---|
case:4,6,8 | case:5,7 | case:6 |
![]() |
![]() |
![]() |
---|---|---|
case:2 | case:1,3 | case:9 |
登入成功后跳轉到相應的后台頁面:
測試項目的運行如下:
5.2 測試數據報告
使用TestNG生成測試報告如下:
六、測試總結
6.1 技術亮點
6.1.1 ajax實現異步交互
使用ajax,通過在后台與服務器進行少量數據交換,就可以使網頁實現異步更新。使得我們可以獲得更多的測試用例分析的情況,如判斷用戶是否存在,存在用戶的密碼是否正確,細化我們的測試分析情況。
下面是登入校驗的核心代碼:
<script>
$("#sign-in-submit").click(function () {
if($("#user").val().length===0){
javaex.tip({
content : "請輸入用戶名",
type : "error"
});
$('#expect_result').val("請輸入用戶名");
return false;
}
if($("#pass").val().length===0){
javaex.tip({
content : "請輸入密碼",
type : "error"
});
$('#expect_result').val("請輸入密碼");
return false;
}
if(!isPermit()){
return false;
}
})
function isPermit(){
var flag = false;
$.ajax({
type:"post",
url : 'springboot/user/login',
data:{username:$('#user').val(),password:$('#pass').val()},
success: function (res) {
$('#expect_result').val(res["msg"])
if(res["type"]==='error'){
javaex.tip({
mode : "message",
content : res["msg"],
type : "error"
});
}
else{
javaex.tip({
mode : "message",
content : res["msg"],
type : "success"
});
flag = true;
}
// 建議延遲加載
setTimeout(function() {
}, 3000);
}
});
return flag;
}
</script>
6.1.2 使用springboot+mybatis-plus框架搭建項目
使用springboot框架能夠快速搭建項目,對主流的開發框架都提供了⽆配置集成(springboot內置了配置),且項⽬可以獨⽴運⾏、⽆需單獨配置servlet容器(內置了tomcat),極⼤提⾼了開發、部署效率,此外還提供了運⾏時監控系統(⽇志等)。
使用mybatis-plus能夠幫我們逆向生成代碼,提高代碼編寫的效率,同時給我們提供好了CRUD接口,能讓我們操作數據庫(稍簡單的業務)時無需編寫sql語句,可以直接調用內置的方法去操作即可。
項目中的使用(部分示例):
![]() |
![]() |
---|---|
springboot工程 | mybatis-plus代碼生成器(逆向工程) |
6.1.3 使用springsecurity安全框架
SpringSecurity 基於 Spring 框架,提供了一套 Web 應用安全性的完整解決方案,能幫助我們更好地實現登入功能的認證和授權。
項目中的使用(部分示例):
![]() |
![]() |
---|
6.1.4 使用EasyExcel工具讀取Excel文件數據
EasyExcel是一個基於Java的簡單、省內存的讀寫Excel的開源項目。在盡可能節約內存的情況下支持讀寫百M的Excel(比POI好)。 github地址:https://github.com/alibaba/easyexcel
項目中的使用:
public static Object[][] getExcelData(){
//讀取Excel文件數據
List<Map<Integer, String>> list = EasyExcel
.read(ExcelPath)
.sheet()
.doReadSync();
Object[][] data = new Object[list.size()][];
int row=0, column;
for (Map<Integer, String> map : list) {
data[row] = new Object[map.size()];
column=0;
for (String value : map.values()) {
data[row][column] = value;
column++;
}
row++;
}
return data;
}
6.1.5 通過IDEA生成TestNG的測試報告
TestNG 默認自帶的有HTML格式的測試報告。這也充分說明拿它來做 UI 自動化測試的優勢。
![]() |
![]() |
---|
6.2 問題分析
在讀取數據庫數據傳值給DataProvider的測試過程中,遇到了個別問題,如下分析:
問題1:
![]() |
![]() |
---|---|
使用Mybatis-plus封裝的CRUD接口查詢數據庫 | 測試時報空指針異常錯誤 |
問題1分析:
此處使用了Mybatis-plus框架,並用框架提供的相關方法查詢數據庫,但是無法將數據查出,報空指針異常,而且框起來那部分代碼是沒問題的,能在junit單元測試上測試成功,不知道使用了框架后,TestNG是否支持,如果支持是否有哪些地方進行改進。在測試中遇到了此問題,上網查幾乎沒有相關解答。
因為測試工程是springboot工程,使用TestNG過程中未解決依賴注入等問題,可能在使用時有其他要求或代碼添加等問題需要改進。涉及這方面的知識,后續再了解一下,是否真的存在相關問題。
上網查了查一些類似問題:
![]() |
![]() |
---|---|
類似問題 | 解決方法 |
此問題目前也沒有明確問題關鍵問題與解答,而提供的解決方式就是——將連接操作數據庫的代碼都直接放在 @DataProvider 中,它就可以正常工作,改后的代碼見下面給出的代碼示例。
涉及這方面的知識,后續有時間再了解查閱一下此方面的問題該要如何解決。因此在測試時就改成了用上面能解決數據讀取的方法。
除了測試學習過程中遇到的一些問題,測試項目本身也有待完善的地方,但由於期末安排緊張,之后有空自己也會完善改進。
6.3 作業總結
通過這次實驗,對軟件測試有了更進一步的學習了解,在測試中遇到的問題,能夠自己尋找解決方案解決,從中也能學習到更多的知識,也能夠將學習到的知識運用到實踐當中,自己可以自覺學習相關專業知識,相信后續自己也會不斷完善相關方面的知識學習,把知識應用的更好。