【軟件測試】Selenium+TestNG 綜合練習


學號: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 中,它就可以正常工作,改后的代碼見下面給出的代碼示例。

image

涉及這方面的知識,后續有時間再了解查閱一下此方面的問題該要如何解決。因此在測試時就改成了用上面能解決數據讀取的方法。

除了測試學習過程中遇到的一些問題,測試項目本身也有待完善的地方,但由於期末安排緊張,之后有空自己也會完善改進。

6.3 作業總結

通過這次實驗,對軟件測試有了更進一步的學習了解,在測試中遇到的問題,能夠自己尋找解決方案解決,從中也能學習到更多的知識,也能夠將學習到的知識運用到實踐當中,自己可以自覺學習相關專業知識,相信后續自己也會不斷完善相關方面的知識學習,把知識應用的更好。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM