通过ChatGPT生成测试用例和测试脚本(2)

简介: 通过ChatGPT生成测试用例和测试脚本

4 GUI的测试代码

4.1为什么要使用GUI的测试代码

在TestRegister.py使用

代码语言:javascript

复制

self.assertIn("showerror(\"1\")",response.text)验证注册成功;
self.assertIn("showerror(\"username\")",response.text)验证用户已经被注册过;
self.assertIn("showerror(\"phone\")",response.text)验证手机号已经被注册过;
self.assertIn("showerror(\"email\")",response.text)验证Email已经被注册过。

原因是,这些信息是通过javascript函数showerror(info)实现的,另外也发现在index.js中类似

代码语言:javascript

复制

const passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{5,30}$/;
if (!passwordPattern.test(password)) {
       document.getElementById('passwordError').innerText = '密码必须包含大小写字母、数字和特殊字符';
        isValid = false;
 }

也没有用过接口测试脚本。这是因为接口测试只负责后端逻辑,而前端逻辑必须由基于GUI的测试代码来生成。

4.2 基于ChatGPT生成GUI代码

输入

对下面用例书写基于playwright+pytest的测试脚本。

前端代码

代码语言:javascript

复制

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>用户注册界面</title>
<link rel="stylesheet" type="text/css" href="css/index.css"/>
<script src="js/index.js" type="text/javascript"></script>
    <script src="https://cdn.jsdelivr.net/npm/crypto-js@4.1.1/core.js"></script>
<script src="https://cdn.jsdelivr.net/npm/crypto-js@4.1.1/sha256.js"></script>
</head>
<body>
<div class="container">
    <h2>用户注册</h2>
     <form name="myForm" method="POST" action="jsp/register.jsp" onsubmit="return validateForm();">
        <label for="username">账号 (5-20位字母或数字):</label>
        <input type="text" id="username" name="username" required>
        <div id="usernameError" class="error"></div>
        <label for="password">密码 (5-30位,包含大小写字母、数字和特殊字符):</label>
        <input type="password" id="password" name="password" required>
        <div id="passwordError" class="error"></div>
        <label for="confirmPassword">密码确认:</label>
        <input type="password" id="confirmPassword" name="confirmPassword" required>
        <div id="confirmPasswordError" class="error"></div>
        <label for="phone">手机号 (中国):</label>
        <input type="tel" id="phone" name="phone" required>
        <div id="phoneError" class="error"></div>
        <label for="email">邮箱:</label>
        <input type="email" id="email" name="email" required>
        <div id="emailError" class="error"></div>
        <button type="submit">注册</button>
    </form>
</div>
</body>
</html>

TC1 - 账号有效性测试:

输入有效的账号(5-20位字母或数字),预期结果:注册成功。

TC2 - 密码长度不足测试:

密码包含数字大小写字母,但是不包含特殊字符,预期结果:显示错误信息"密码必须是5-30位,包含大小写字母、数字和特殊字符"。

TC3 - 手机号格式错误测试:

输入非法的手机号码格式,预期结果:显示错误信息"手机号格式错误"。

TC4 – 一个用户重复注册两次:

一个用户重复注册两次,预期结果:用户名已经被注册过!"。

输出

代码语言:javascript

复制

Test_register_GUI.py
import pytest
from playwright.sync_api import sync_playwright
           
# 测试类
class TestUserRegistration:
    @pytest.fixture(scope="module")
    def setup(self):
        with sync_playwright() as p:
            self.browser = p.chromium.launch(headless=False)  # 设为 True 可以在后台运行
            self.page = self.browser.new_page()
            self.page.goto('file:///path/to/your/html/file.html')  # 请替换为你的 HTML 文件路径
            yield
            self.browser.close()
         
    def test_valid_username(self, setup):
        self.page.fill('input[name="username"]', 'validUser123')
        self.page.fill('input[name="password"]', 'ValidPass123!')
        self.page.fill('input[name="confirmPassword"]', 'ValidPass123!')
        self.page.fill('input[name="phone"]', '13800138000')
        self.page.fill('input[name="email"]', 'test@example.com')
        self.page.click('button[type="submit"]')
        # 检查注册是否成功(这里需要根据实际页面反馈调整)
        assert self.page.locator('text=注册成功').is_visible()  
         
    def test_password_length_error(self, setup):
        self.page.fill('input[name="username"]', 'testUser')
        self.page.fill('input[name="password"]', 'NoSpecialChar1')
        self.page.fill('input[name="confirmPassword"]', 'NoSpecialChar1')
        self.page.fill('input[name="phone"]', '13800138000')
        self.page.fill('input[name="email"]', 'test@example.com')
        self.page.click('button[type="submit"]')
        # 检查错误信息
        assert self.page.locator('text=密码必须是5-30位,包含大小写字母、数字和特殊字符').is_visible()
         
    def test_invalid_phone_format(self, setup):
        self.page.fill('input[name="username"]', 'testUser')
        self.page.fill('input[name="password"]', 'ValidPass123!')
        self.page.fill('input[name="confirmPassword"]', 'ValidPass123!')
        self.page.fill('input[name="phone"]', '12345')  # 非法手机号
        self.page.fill('input[name="email"]', 'test@example.com')
        self.page.click('button[type="submit"]')
        # 检查错误信息
        assert self.page.locator('text=手机号格式错误').is_visible()
           
    def test_duplicate_registration(self, setup):
        # 第一次注册
        self.page.fill('input[name="username"]', 'duplicateUser')
        self.page.fill('input[name="password"]', 'ValidPass123!')
        self.page.fill('input[name="confirmPassword"]', 'ValidPass123!')
        self.page.fill('input[name="phone"]', '13800138000')
        self.page.fill('input[name="email"]', 'duplicate@example.com')
        self.page.click('button[type="submit"]')
        # 检查注册成功
        assert self.page.locator('text=注册成功').is_visible()
        # 第二次注册相同用户
        self.page.fill('input[name="username"]', 'duplicateUser')
        self.page.fill('input[name="password"]', 'ValidPass123!')
        self.page.fill('input[name="confirmPassword"]', 'ValidPass123!')
        self.page.fill('input[name="phone"]', '13800138000')
        self.page.fill('input[name="email"]', 'duplicate@example.com')
        self.page.click('button[type="submit"]')
        # 检查错误信息
        assert self.page.locator('text=用户名已经被注册过!').is_visible()

说明:

路径替换:请确保在 self.page.goto() 中替换为您的 HTML 文件的正确路径。

错误信息检查:根据您的实际页面反馈,可能需要调整断言中的文本。

浏览器设置:可以根据需要设置 headless 参数来选择是否在后台运行浏览器。

运行测试:确保已安装 pytest 和 playwright,可以通过 pytest 命令运行测试。

请根据您的实际需求调整测试脚本。

4.3 调整

4.3.1 调整GUI的测试代码

修改

代码语言:javascript

复制

self.page.goto('file:///path/to/your/html/file.html')

代码语言:javascript

复制

self.page.goto('http://127.0.0.1:8080/CharGPTEbusiness/register.jsp')

在测试用例

test_duplicate_registration中,由于self.page.fill('input[name="phone"]', '13800138000')

在测试用例test_valid_username也用的是这个手机号,所以改为

代码语言:javascript

复制

self.page.fill('input[name="phone"]', '13800138001')

# 第二次注册相同用户

代码语言:javascript

复制

page.fill('input[name="phone"]', '13800138001')

另外抽出去掉stup()方法,抽出REGISTER_URL作为全局变量

代码语言:javascript

复制

REGISTER_URL = 'http://127.0.0.1:8080/CharGPTEbusiness/register.html'

改造每个几个方法

代码语言:javascript

复制

# 测试类
class TestUserRegistration:   
def test_valid_username(self,page: Page):
         page.goto(REGISTER_URL)
def test_password_length_error(self,page: Page):
        page.goto(REGISTER_URL)

最后与接口测试用例一样,为每个程序结束增加一个情况数据库的方法

代码语言:javascript

复制

def init_db():# 设置数据库连接参数
    host = 'localhost'
    user = 'root'
    password = '123456'
    database = 'ebusiness'
    # 创建数据库连接
    connection = pymysql.connect(host=host, user=user, password=password, database=database)
    try:
        with connection.cursor() as cursor:  
            # SQL语句,用于清空user表
            sql = "TRUNCATE TABLE user"
        
            # 执行SQL语句
            cursor.execute(sql)
        
            # 提交事务
            connection.commit()
    finally:
        # 关闭数据库连接
        connection.close()

通过

代码语言:javascript

复制

pytest -sv .\Test_register_GUI.py –headed

命令运行

4.3.2 调整合并的测试代码

在接口代码和GUI代码都是用热清空数据库的操作,现在来进行统一的封装合并。

建立Util.py

代码语言:javascript

复制

import pymysql
         
class ClassDB:
    def init_db():# 设置数据库连接参数  
        host = 'localhost'
        user = 'root'
        password = '123456'
        database = 'ebusiness'
        # 创建数据库连接
        connection = pymysql.connect(host=host, user=user, password=password, database=database)
        try:
            with connection.cursor() as cursor:
                # SQL语句,用于清空user表
                sql = "TRUNCATE TABLE user"
        
                # 执行SQL语句
                cursor.execute(sql)
        
                # 提交事务
                connection.commit()
        finally:
            # 关闭数据库连接
            connection.close()

修改Test_register_GUI.py

代码语言:javascript

复制

from Util import ClassDB  
         
REGISTER_URL = 'http://127.0.0.1:8080/CharGPTEbusiness/register.html'
# 测试类
class TestUserRegistration:   
    def teardown_class(self):
        ClassDB.init_db()
修改TestRegister.py
from Util import ClassDB
    def teardown_class(self):
        ClassDB.init_db()

5 单元测试

到目前位置存在以下几个后端java文件

validateUser.java、DBuser.java、User.java

我们针对这些代码书写单元测试用例

5.1单元测试代码的申请

输入

请针对以下几个java文件形成单元测试用例

代码语言:javascript

复制

validateUser.java  
package ebusiness;
import java.util.regex.Pattern;
         
public class validateUser {
       public String validate(User user) {
        String usernamePattern = "^[a-zA-Z0-9]{5,20}$";
        String phonePattern = "^1[3-9]\\d{9}$";
        String emailPattern = "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$";
         
        if (!Pattern.matches(usernamePattern, user.getUsername())) {
            return "账号必须是5-20位字母或数字";
        }
        if (!Pattern.matches(phonePattern, user.getPhone())) {
            return "手机号必须符合中国手机号码格式";
        }
        if (!Pattern.matches(emailPattern, user.getEmail())) {
            return "Email格式不正确";
        }
        return "验证成功";
    }
}

DBuser.java

代码语言:javascript

复制

package ebusiness;
         
import java.sql.*;
         
public class DBuser {
    private static final String url = "jdbc:mysql://localhost:3306/ebusiness";
    private static final String user = "root";
    private static final String password = "123456";
    private static Statement stmt = null;
    private static ResultSet rs;
    
    public Connection connect() throws SQLException {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            Connection conn = DriverManager.getConnection(url, user, password);
            stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
            return conn;
        } catch (ClassNotFoundException e) {  
            System.out.println("JDBC Driver not found: " + e.getMessage());
        } catch (SQLException e) {
            System.out.println("Database connection failed: " + e.getMessage());
        }
        return null;
    }
    
    public void disconnect(Connection conn) {
        try {
            if (rs != null) {
                rs.close();
            }
            stmt.close();
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public String insert(Connection conn, User user) throws SQLException {
        if (! userExists(conn, user.getUsername(), user.getEmail(), user.getPhone()).equals("0")) {  
            return userExists(conn, user.getUsername(), user.getEmail(), user.getPhone());
        }
        String insertString = "INSERT INTO user (username, password, phone, email) VALUES (?, ?, ?, ?)";
        try {
            PreparedStatement ps = conn.prepareStatement(insertString);
            ps.setString(1, user.getUsername());
            ps.setString(2, user.getPassword());
            ps.setString(3, user.getPhone());
            ps.setString(4, user.getEmail());
            int affectedRows = ps.executeUpdate();
            return String.valueOf(affectedRows);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return "-1";
    }
    
    public String userExists(Connection conn, String username, String email, String phone) throws SQLException {  
           String queryString = "SELECT * FROM user WHERE username = ?";
        try {
            PreparedStatement ps = conn.prepareStatement(queryString);
            ps.setString(1, username);
            ResultSet rs = ps.executeQuery();
            if(rs.next()) 
                   return "username"; 
        } catch (SQLException e) {
            e.printStackTrace();
        }
        queryString = "SELECT * FROM user WHERE email = ?";
        try {
            PreparedStatement ps = conn.prepareStatement(queryString);
            ps.setString(1, email);
            ResultSet rs = ps.executeQuery();
            if(rs.next()) 
                   return "email"; 
        } catch (SQLException e) {
            e.printStackTrace();
        }
        queryString = "SELECT * FROM user WHERE phone = ?";
        try {  
            PreparedStatement ps = conn.prepareStatement(queryString);
            ps.setString(1, phone);
            ResultSet rs = ps.executeQuery();
            if(rs.next())
                   return "phone"; 
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return "0";
    }
}

User.java

代码语言:javascript

复制

package ebusiness;
         
public class User {
    private int id;
    private String username;
    private String password;
    private String phone;
    private String email;
         
    // Getters and Setters  
    public int getId() {
        return id;
    }
         
    public void setId(int id) {
        this.id = id;
    }
         
    public String getUsername() {
        return username;
    }
         
    public void setUsername(String username) {
        this.username = username;
    }
         
    public String getPassword() {
        return password;
    }
         
    public void setPassword(String password) {
        this.password = password;  
    }
         
    public String getPhone() {
        return phone;
    }
         
    public void setPhone(String phone) {
        this.phone = phone;
    }
         
    public String getEmail() {
        return email;
    }
         
    public void setEmail(String email) {
        this.email = email;
    }
}

5.2ChatGPT给出单元测试代码

为你的 Java 类 validateUser, DBuser, 和 User 编写单元测试用例,可以使用 JUnit 框架。以下是如何为这些类创建基本的单元测试的示例。

1. validateUser 类的单元测试

首先,我们需要测试 validateUser 类中的 validate 方法。我们将创建一个测试类 ValidateUserTest。

代码语言:javascript

复制

package ebusiness;
         
import org.junit.jupiter.api.Test;
         
import static org.junit.jupiter.api.Assertions.assertEquals;
         
public class ValidateUserTest {
         
    @Test
    public void testValidUser() {
        validateUser validator = new validateUser();
        User user = new User();
        user.setUsername("validUser");
        user.setPhone("13812345678");
        user.setEmail("test@example.com");
         
        String result = validator.validate(user);
        assertEquals("验证成功", result);  
    }
         
    @Test
    public void testInvalidUsername() {
        validateUser validator = new validateUser();
        User user = new User();
        user.setUsername("usr"); // Invalid username
        user.setPhone("13812345678");
        user.setEmail("test@example.com");
         
        String result = validator.validate(user);
        assertEquals("账号必须是5-20位字母或数字", result);
    }
         
    @Test
    public void testInvalidPhone() {
        validateUser validator = new validateUser();
        User user = new User();
        user.setUsername("validUser");
        user.setPhone("1234567890"); // Invalid phone
        user.setEmail("test@example.com");
           
        String result = validator.validate(user);
        assertEquals("手机号必须符合中国手机号码格式", result);
    }
         
    @Test
    public void testInvalidEmail() {
        validateUser validator = new validateUser();
        User user = new User();
        user.setUsername("validUser");
        user.setPhone("13812345678");
        user.setEmail("invalid-email"); // Invalid email
         
        String result = validator.validate(user);
        assertEquals("Email格式不正确", result);
    }
}

2. DBuser 类的单元测试

DBuser 类的测试需要模拟数据库连接。可以使用 Mockito 来模拟数据库操作。以下是一个简单的测试示例。

代码语言:javascript

复制

package ebusiness;
         
import org.junit.jupiter.api.BeforeEach;  
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
         
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
         
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;
         
public class DBuserTest {
         
    private DBuser dbUser;
    private Connection mockConnection;
         
    @BeforeEach
    public void setUp() {
        dbUser = new DBuser();
        mockConnection = Mockito.mock(Connection.class);
    }
           
    @Test
    public void testInsertUser() throws SQLException {
        User user = new User();
        user.setUsername("newUser");
        user.setPassword("password123");
        user.setPhone("13812345678");
        user.setEmail("newuser@example.com");
         
        // Mocking userExists to return "0" (meaning user does not exist)
        when(dbUser.userExists(mockConnection, user.getUsername(), user.getEmail(), user.getPhone())).thenReturn("0");
         
        // Mocking PreparedStatement and ResultSet
        PreparedStatement mockStatement = Mockito.mock(PreparedStatement.class);
        when(mockConnection.prepareStatement(anyString())).thenReturn(mockStatement);
        when(mockStatement.executeUpdate()).thenReturn(1); // Simulate successful insert
         
        String result = dbUser.insert(mockConnection, user);  
        assertEquals("1", result); // Expecting 1 row affected
    }
         
    @Test
    public void testUserExists() throws SQLException {
        User user = new User();
        user.setUsername("existingUser");
        user.setEmail("existing@example.com");
        user.setPhone("13812345678");
         
        // Mocking ResultSet
        ResultSet mockResultSet = Mockito.mock(ResultSet.class);
        when(mockResultSet.next()).thenReturn(true); // Simulate user found
        when(mockConnection.prepareStatement(anyString())).thenReturn(mockStatement);
        when(mockStatement.executeQuery()).thenReturn(mockResultSet);
         
        String result = dbUser.userExists(mockConnection, user.getUsername(), user.getEmail(), user.getPhone());  
        assertEquals("username", result); // Expecting "username" since user exists
    }
}

3. User 类的单元测试

User 类主要是一个数据容器,通常不需要单元测试,除非你希望测试其 getter 和 setter 方法。以下是一个简单的示例:

代码语言:javascript

复制

package ebusiness;
         
import org.junit.jupiter.api.Test;
         
import static org.junit.jupiter.api.Assertions.assertEquals;
         
public class UserTest {
         
    @Test
    public void testUserGettersAndSetters() {
        User user = new User();
        user.setId(1);
        user.setUsername("testUser");
        user.setPassword("password");
        user.setPhone("13812345678");  
        user.setEmail("test@example.com");
         
        assertEquals(1, user.getId());
        assertEquals("testUser", user.getUsername());
        assertEquals("password", user.getPassword());
        assertEquals("13812345678", user.getPhone());
        assertEquals("test@example.com", user.getEmail());
    }
}

4. 运行测试

确保在你的项目中添加 JUnit 和 Mockito 的依赖(如果你使用的是 Maven,可以在 pom.xml 中添加相应的依赖)。然后使用 IDE命令行工具运行测试。

以上是为你的 Java 类编写单元测试的基本示例,具体的实现可能需要根据你的项目结构和需求进行调整。

单元测试代码的运行

5.3运行单元测试代码

5.3.1运行ValidateUserTest.java

运行ValidateUserTest.java测试通过;

5.3.2 运行DBuserTest.java

运行DBuserTest.java测试通过;

在pom.xml中加上

代码语言:javascript

复制

<dependency>
      <groupId>org.mockito</groupId> 
      <artifactId>mockito-core</artifactId> 
      <version>3.9.0</version>
      <scope>test</scope> 
</dependency>

告知

mockStatement cannot be resolved to a variable DBuserTest.java /ChatGPTEbusiness/src/test/java/ebusiness line 56 Java Problem

询问ChatGPT给出修正后的代码

代码语言:javascript

复制

@Test
    public void testUserExists() throws SQLException {
               User user = new User();
               user.setUsername("existingUser");
               user.setEmail("existing@example.com");
               user.setPhone("13812345678");
         
               // Mocking ResultSet
               ResultSet mockResultSet = Mockito.mock(ResultSet.class);
               when(mockResultSet.next()).thenReturn(true); // Simulate user found  
         
               // Mocking PreparedStatement
               PreparedStatement mockStatement = Mockito.mock(PreparedStatement.class);
               when(mockConnection.prepareStatement(anyString())).thenReturn(mockStatement);
               when(mockStatement.executeQuery()).thenReturn(mockResultSet);
         
        String result = dbUser.userExists(mockConnection, user.getUsername(), user.getEmail(), user.getPhone());
        assertEquals("username", result); // Expecting "username" since user exists
}

运行testUserExists(),但是testInsertUser()报空指针异常,没有找到问题。(有知道原因的朋友请告诉我)

5.3.2 运行UserTest.java

运行UserTest.java测试通过;

6 代码评审

到目前位置存在以下几个文件

前端代码

register.html、index.js、showerror.js、index.css

后端代码

register.jsp、validateUser.java、DBuser.java、User.java

API测试代码

TestRegister.py

GUI测试代码

Test_register_GUI.py

单元测试代码

ValidateUserTest.java、DBuserTest.java、UserTest.java

6.1 前端代码

6.1.1提出前端代码评审请求

请对前端代码进行评审

register.html

代码语言:javascript

复制

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,
initial-scale=1.0">
<title>用户注册界面</title>
 <link
rel="stylesheet" type="text/css"
href="css/index.css"/>
 <script
src="js/index.js" type="text/javascript"></script>
<script
src="https://cdn.jsdelivr.net/npm/crypto-js@4.1.1/core.js"></script>
 <script
src="https://cdn.jsdelivr.net/npm/crypto-js@4.1.1/sha256.js"></script>
</head>
<body>
<div class="container">
<h2>用户注册</h2>
<form name="myForm" method="POST"
action="jsp/register.jsp" onsubmit="return
validateForm();">
<label for="username">账号 (5-20位字母或数字):</label>
<input type="text" id="username"
name="username" required>
<div id="usernameError"
class="error"></div>
<label for="password">密码 (5-30位,包含大小写字母、数字和特殊字符):</label>
<input type="password" id="password"
name="password" required>
<div id="passwordError"
class="error"></div>
<label for="confirmPassword">密码确认:</label>
<input type="password" id="confirmPassword"
name="confirmPassword" required>
<div id="confirmPasswordError"
class="error"></div>
<label for="phone">手机号 (中国):</label>
<input type="tel" id="phone"
name="phone" required>
<div id="phoneError"
class="error"></div>
<label for="email">邮箱:</label>
<input type="email" id="email"
name="email" required>
<div id="emailError"
class="error"></div>
<button type="submit">注册</button>
</form>
</div>
</body>
</html>

index.css

代码语言:javascript

复制

<style>
        body {
            font-family: Arial, sans-serif;
            padding: 20px;
            background-color: #f4f4f4;
        }
        .container {
            max-width: 400px;
            margin: auto;
            padding: 20px;
            background-color: #fff;
            border-radius: 10px;
            box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
        }
        input[type="text"], input[type="password"], input[type="email"], input[type="tel"] {
            width: 100%;
            padding: 10px;
            margin: 10px 0;
            border: 1px solid #ccc;
            border-radius: 5px;
        }
        button {
            width: 100%;
            padding: 10px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 5px;
            font-size: 16px;
        }
        button:hover {
            background-color: #45a049;
        }
        .error {
            color: red;
            font-size: 12px;
        }
</style>

index.js

代码语言:javascript

复制

function validateForm() {
        const username = document.getElementById('username').value;
        const password = document.getElementById('password').value;
        const confirmPassword = document.getElementById('confirmPassword').value;
        const phone = document.getElementById('phone').value;
        const email = document.getElementById('email').value;
         
        let isValid = true;
        document.getElementById('usernameError').innerText = '';
        document.getElementById('passwordError').innerText = '';
        document.getElementById('confirmPasswordError').innerText = '';
        document.getElementById('phoneError').innerText = '';
        document.getElementById('emailError').innerText = '';
         
        const usernamePattern = /^[a-zA-Z0-9]{5,20}$/;
        if (!usernamePattern.test(username)) {
            document.getElementById('usernameError').innerText = '账号必须是5-20位的字母或数字';  
            isValid = false;
        }
         
        const passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{5,30}$/;
        if (!passwordPattern.test(password)) {
            document.getElementById('passwordError').innerText = '密码必须包含大小写字母、数字和特殊字符';
            isValid = false;
        }
         
        if (password !== confirmPassword) {
            document.getElementById('confirmPasswordError').innerText = '密码确认不一致';
            isValid = false;
        }
         
        const phonePattern = /^1[3-9]\d{9}$/;
        if (!phonePattern.test(phone)) {
            document.getElementById('phoneError').innerText = '请输入有效的中国手机号';
            isValid = false;  
        }
         
        const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
        if (!emailPattern.test(email)) {
            document.getElementById('emailError').innerText = '请输入有效的Email地址';
            isValid = false;
        }
         
        if (isValid) {
                     // 获取密码并进行SHA-256哈希
                     let passwordField = document.forms["myForm"]["password"];
                     let hashedPassword = CryptoJS.SHA256(password).toString(CryptoJS.enc.Base64);
        
                     // 将哈希后的密码重新赋值给表单的密码字段
                     passwordField.value = hashedPassword;
                     console.log('加密后的密码:', hashedPassword);
            alert('注册成功!');
        }
           
        return isValid;
    }

5.4 showerror.js

代码语言:javascript

复制

function showerror(info) {
       switch(info){
              case("username"):
                     document.getElementById('usernameError').innerText = '用户名已经被注册过!';
                     break;
              case("email"):
                     document.getElementById('emailError').innerText = '电子邮件已经被注册过!';
                     break;
              case("phone"):
                     document.getElementById('phoneError').innerText = '手机已经被注册过!';
                     break;
              case("1"):
                     document.writeln("注册成功!");
                     break;
              default:
                     document.writeln('注册成功!');  
       }
}

6.1.2前端代码评审回复

你的前端代码整体结构良好,包含了用户注册所需的基本功能,但仍有一些地方可以改进。以下是对 register.html、index.css、index.js 和 showerror.js 的评审和建议:

1. HTML (register.html)

优点:

清晰的表单结构,标签和输入框的配对良好。

使用了 required 属性来确保用户输入。

改进建议:

表单的 action 属性:建议使用更现代的方式处理表单提交,比如使用 AJAX,而不是直接提交到 jsp/register.jsp。这样可以提高用户体验。

错误信息显示:目前错误信息是通过 div 显示的,可以考虑在表单提交后通过 AJAX 获取并更新这些信息,而不是在页面上直接输出。

安全性:考虑使用 HTTPS 来保护用户数据。

2. CSS (index.css)

优点:

代码结构清晰,使用了 JSP 标签。

改进建议:

CSS 文件命名:文件名 index.css 可能不够具体,建议使用 register.css 或类似名称以提高可读性。改为register.css

分离逻辑和样式:尽量将 CSS 和 JSP 逻辑分开,保持 CSS 文件只包含样式,而不包含 Java 逻辑。CSS中没有包含逻辑

3. JavaScript (index.js)

优点:

使用正则表达式进行输入验证,确保用户输入符合要求。

在输入不合法时,及时给出反馈。

改进建议

密码哈希:建议使用 HTTPS 进行数据传输,虽然你在客户端进行了 SHA-256 哈希,但这并不能替代后端的安全措施。

用户体验:注册成功的提示使用了 alert,可以考虑使用更友好的方式,比如在页面上显示成功消息,而不是弹出框。(取消alert,改为console.log)

代码重复:在validateForm中的错误信息显示逻辑可以提取成一个函数,以减少重复代码。

4. 响应处理 (showerror.js)

优点:

使用 switch 语句处理不同的错误消息,逻辑清晰。

改进建议:

DOM 操作:使用 document.writeln 不是最佳实践,建议使用 innerText 或 innerHTML 来更新页面内容。(以后改为重定向)

函数的通用性:可以考虑将错误信息的处理逻辑封装成一个更通用的函数,接受错误类型和目标元素作为参数。(目前错误信息比较少,暂不考虑)

5. 其他建议

输入验证:可以在后端再次进行输入验证,确保数据的安全性和有效性。(的确如此处理)

用户体验:在注册成功或失败后,可以考虑重定向用户到登录页面或其他页面,而不是停留在注册页面。(下面考虑)

代码注释:在 JavaScript 和 JSP 中添加适当的注释,帮助其他开发者理解代码逻辑。

总结

你的前端代码已经有了良好的基础,经过一些改进后可以提升用户体验和代码的可维护性。希望这些建议对你有所帮助!

6.1.3 封装validateForm()函数

输入

对于validateForm 中的错误信息显示逻辑可以提取成一个函数,以减少重复代码。请帮助实现下。

输出

当然可以!为了减少重复代码,我们可以创建一个辅助函数来处理错误信息的显示。在 validateForm 函数中,当验证失败时,我们调用这个新函数来更新对应的错误信息。以下是修改后的代码示例:  

修改后的 index.js

代码语言:javascript

复制

function validateForm() {
    const username = document.getElementById('username').value;
    const password = document.getElementById('password').value;
    const confirmPassword = document.getElementById('confirmPassword').value;  
    const phone = document.getElementById('phone').value;
    const email = document.getElementById('email').value;
         
    let isValid = true;
    clearErrorMessages(); // 清空所有错误信息
         
    const usernamePattern = /^[a-zA-Z0-9]{5,20}$/;
    if (!usernamePattern.test(username)) {
        showError('usernameError', '账号必须是5-20位的字母或数字');
        isValid = false;
    }
         
    const passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{5,30}$/;
    if (!passwordPattern.test(password)) {
        showError('passwordError', '密码必须包含大小写字母、数字和特殊字符');
        isValid = false;
    }
         
    if (password !== confirmPassword) {
        showError('confirmPasswordError', '密码确认不一致');  
        isValid = false;
    }
         
    const phonePattern = /^1[3-9]\d{9}$/;
    if (!phonePattern.test(phone)) {
        showError('phoneError', '请输入有效的中国手机号');
        isValid = false;
    }
         
    const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
    if (!emailPattern.test(email)) {
        showError('emailError', '请输入有效的Email地址');
        isValid = false;
    }
         
    if (isValid) {
        // 获取密码并进行SHA-256哈希
        let passwordField = document.forms["myForm"]["password"];
        let hashedPassword = CryptoJS.SHA256(password).toString(CryptoJS.enc.Base64);
          
        // 将哈希后的密码重新赋值给表单的密码字段
        passwordField.value = hashedPassword;
        console.log('加密后的密码:', hashedPassword);
        console.log ('注册成功!');
    }
         
    return isValid;
}
         
// 新增的函数,用于显示错误信息
function showError(elementId, message) {
    document.getElementById(elementId).innerText = message;
}
         
// 新增的函数,用于清空所有错误信息
function clearErrorMessages() {
    document.getElementById('usernameError').innerText = '';
    document.getElementById('passwordError').innerText = '';
    document.getElementById('confirmPasswordError').innerText = '';
    document.getElementById('phoneError').innerText = '';
    document.getElementById('emailError').innerText = '';
}

说明

showError 函数:此函数接收两个参数:elementId(要显示错误信息的元素的 ID)和 message(要显示的错误信息)。它会将指定元素的文本设置为传入的错误信息。

clearErrorMessages 函数:此函数用于清空所有错误信息,确保在每次验证之前,之前的错误信息都被清除。

在 validateForm 中的调用:

在每次验证失败时,调用 showError 函数来显示对应的错误信息。

在函数开始时调用 clearErrorMessages,以确保每次验证时错误信息都是干净的。

通过这种方式,我们减少了重复代码,提高了代码的可读性和可维护性。希望这对你有帮助!

修改后,运行Test_register_GUI.py。保证没有引入缺陷

6.2 后端代码

6.2.1提出后端代码评审请求

请对后端代码进行评审

register.jsp

代码语言:javascript

复制

<%@ page contentType="text/html; charset=utf-8" %>
<%@ page import="java.sql.*, javax.sql.*" %>
<%@ page import="ebusiness.*" %> 
<%
    String username = request.getParameter("username");
    String password = request.getParameter("password"); 
    String phone = request.getParameter("phone");
    String email = request.getParameter("email");
    User user = new User();
    user.setUsername(username);
    user.setPassword(password);
    user.setPhone(phone);
    user.setEmail(email);
validateUser vu = new validateUser();
if(vu.validate(user).equals("验证成功")){
try {
DBuser mysql = new DBuser();
Connection conn = mysql.connect();
String info = mysql.insert(conn,user);
if (info.equals("1")) 
out.println("注册成功");
%>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>用户注册界面</title>
<link rel="stylesheet" type="text/css" href="../css/register.css"/>
<script src="../js/index.js" type="text/javascript"></script>
<script src="../js/showerror.js" type="text/javascript"></script>
    <script src="https://cdn.jsdelivr.net/npm/crypto-js@4.1.1/core.js"></script>
<script src="https://cdn.jsdelivr.net/npm/crypto-js@4.1.1/sha256.js"></script>
</head>
<body onload='showerror("<%=info%>");'>
<div class="container">
    <h2>用户注册</h2>
     <form name="myForm" method="POST" action="jsp/register.jsp" onsubmit="return validateForm();">
        <label for="username">账号 (5-20位字母或数字):</label>
        <input type="text" id="username" name="username" required>
        <div id="usernameError" class="error"></div>
        <label for="password">密码 (5-30位,包含大小写字母、数字和特殊字符):</label>
        <input type="password" id="password" name="password" required>
        <div id="passwordError" class="error"></div>
        <label for="confirmPassword">密码确认:</label>
        <input type="password" id="confirmPassword" name="confirmPassword" required>
        <div id="confirmPasswordError" class="error"></div>
        <label for="phone">手机号 (中国):</label>
        <input type="tel" id="phone" name="phone" required>
        <div id="phoneError" class="error"></div>
        <label for="email">邮箱:</label>
        <input type="email" id="email" name="email" required>
        <div id="emailError" class="error"></div>
        <button type="submit">注册</button>
    </form>
</div>
</body>
</html>
<%
mysql.disconnect(conn);
} catch (Exception e) {
out.println(e.getMessage());
}
}else{
out.print(vu.validate(user));
}
%>

validateUser.java

代码语言:javascript

复制

package ebusiness;
import java.util.regex.Pattern;
         
public class validateUser {
       public String validate(User user) {
        String usernamePattern = "^[a-zA-Z0-9]{5,20}$";
        String phonePattern = "^1[3-9]\\d{9}$";
        String emailPattern = "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$";
         
        if (!Pattern.matches(usernamePattern, user.getUsername())) {
            return "账号必须是5-20位字母或数字";
        }
        if (!Pattern.matches(phonePattern, user.getPhone())) {  
            return "手机号必须符合中国手机号码格式";
        }
        if (!Pattern.matches(emailPattern, user.getEmail())) {
            return "Email格式不正确";
        }
        return "验证成功";
    }
}

DBuser.java

代码语言:javascript

复制

package ebusiness;
         
import java.sql.*;
         
public class DBuser {
    private static final String url = "jdbc:mysql://localhost:3306/ebusiness";
    private static final String user = "root";
    private static final String password = "123456";
    private static Statement stmt = null;
    private static ResultSet rs;
    
    public Connection connect() throws SQLException {
        try {  
            Class.forName("com.mysql.cj.jdbc.Driver");
            Connection conn = DriverManager.getConnection(url, user, password);
            stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
            return conn;
        } catch (ClassNotFoundException e) {
            System.out.println("JDBC Driver not found: " + e.getMessage());
        } catch (SQLException e) {
            System.out.println("Database connection failed: " + e.getMessage());
        }
        return null;
    }
    
    public void disconnect(Connection conn) {
        try {
            if (rs != null) {
                rs.close();
            }
            stmt.close();  
            conn.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public String insert(Connection conn, User user) throws SQLException {
        if (! userExists(conn, user.getUsername(), user.getEmail(), user.getPhone()).equals("0")) {
            return userExists(conn, user.getUsername(), user.getEmail(), user.getPhone());
        }
        String insertString = "INSERT INTO user (username, password, phone, email) VALUES (?, ?, ?, ?)";
        try {
            PreparedStatement ps = conn.prepareStatement(insertString);
            ps.setString(1, user.getUsername());
            ps.setString(2, user.getPassword());
            ps.setString(3, user.getPhone());
            ps.setString(4, user.getEmail());
            int affectedRows = ps.executeUpdate();
            return String.valueOf(affectedRows);  
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return "-1";
    }
    
    public String userExists(Connection conn, String username, String email, String phone) throws SQLException {
           String queryString = "SELECT * FROM user WHERE username = ?";
        try {
            PreparedStatement ps = conn.prepareStatement(queryString);
            ps.setString(1, username);
            ResultSet rs = ps.executeQuery();
            if(rs.next()) 
                   return "username";
        } catch (SQLException e) {
            e.printStackTrace();
        }
        queryString = "SELECT * FROM user WHERE email = ?";
        try {
            PreparedStatement ps = conn.prepareStatement(queryString);
            ps.setString(1, email);  
            ResultSet rs = ps.executeQuery();
            if(rs.next()) 
                   return "email";
        } catch (SQLException e) {
            e.printStackTrace();
        }
        queryString = "SELECT * FROM user WHERE phone = ?";
        try {
            PreparedStatement ps = conn.prepareStatement(queryString);
            ps.setString(1, phone);
            ResultSet rs = ps.executeQuery();
            if(rs.next())
                   return "phone";
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return "0";
    }
}

User.java

代码语言:javascript

复制

package ebusiness;
           
public class User {
    private int id;
    private String username;
    private String password;
    private String phone;
    private String email;
         
    // Getters and Setters
    public int getId() {
        return id;
    }
         
    public void setId(int id) {
        this.id = id;
    }
         
    public String getUsername() {
        return username;
    }
         
    public void setUsername(String username) {
        this.username = username;  
    }
         
    public String getPassword() {
        return password;
    }
         
    public void setPassword(String password) {
        this.password = password;
    }
         
    public String getPhone() {
        return phone;
    }
         
    public void setPhone(String phone) {
        this.phone = phone;
    }
         
    public String getEmail() {
        return email;
    }
           
    public void setEmail(String email) {
        this.email = email;
    }
}

6.2.2后端代码评审回复

你的后端代码整体结构清晰,功能实现较为完整,但仍有一些地方可以改进。以下是对 register.jsp、validateUser.java、DBuser.java 和 User.java 的评审和建议:

1. register.jsp

优点:

表单结构清晰,能够处理用户注册逻辑。

使用了validateUser 类进行用户输入的验证。

改进建议:

业务逻辑与视图分离:建议将业务逻辑(如数据库操作和验证)移到 Servlet 或其他控制器中,而不是直接在 JSP 中处理。这样可以更好地遵循 MVC 模式。

错误处理:在发生异常时,建议返回用户友好的错误信息,而不是直接打印异常信息。可以考虑使用日志记录异常。

安全性:在处理用户密码时,确保密码在存储前进行加密处理,而不是在数据库中以明文形式存储。

HTML 输出:在 body 标签的 onload 中直接调用 showerror 函数并传递 info,这可能会导致 XSS 攻击。建议使用 HTML 实体编码来避免潜在的安全问题(考虑避免)。

2. validateUser.java

优点

使用正则表达式进行输入验证,逻辑清晰。

改进建议

返回值设计:返回字符串作为验证结果的方式不够灵活,建议使用一个枚举类型或自定义异常来处理不同的验证错误,以提高代码的可读性和可维护性。

扩展性:如果将来需要添加更多的验证规则,可能会导致代码膨胀。可以考虑将验证逻辑抽象成接口,使用策略模式来处理不同的验证规则。

3. DBuser.java

优点

使用了 PreparedStatement,有效防止 SQL 注入。

连接和断开数据库的逻辑封装得当。

改进建议:

连接管理:建议使用连接池来管理数据库连接,以提高性能和资源利用率。

异常处理:在数据库操作时,建议抛出自定义异常而不是直接打印堆栈跟踪,这样可以更好地处理错误并提供用户友好的反馈。

代码重复:在userExists 方法中,查询用户的逻辑可以提取成一个通用的方法,以减少代码重复。

4. User.java

优点:

简单明了的用户模型。

改进建议:

数据验证:可以在 User 类中添加数据验证方法,以确保在设置属性时符合要求,进一步增强代码的健壮性。

构造函数:可以考虑添加构造函数,以便在创建 User 对象时直接传入参数。

总结

你的后端代码已经具备了基本的功能,但通过一些结构上的调整和安全性的增强,可以使其更加健壮和易于维护。希望这些建议对你有所帮助!

6.3测试代码

测试代码包括API测试代码和GUI测试代码

6.3.1提出测试代码评审请求

请对测试代码进行评审

TestRegister.py

代码语言:javascript

复制

import requests
import unittest
import hashlib
import re
from Util import ClassDB
        
def hash_password(password):
    """对密码进行SHA256散列"""
    return hashlib.sha256(password.encode('utf-8')).hexdigest()  
         
class TestUserRegistration(unittest.TestCase):
    # 注册接口的URL
    def setUp(self):
        self.REGISTER_URL = "http://127.0.0.1:8080/CharGPTEbusiness/jsp/register.jsp"
        self.login_url ="http://127.0.0.1:8080/CharGPTEbusiness/register.jsp"
        self.session = requests.Session()
        response = self.session.get(self.login_url)
        self.token = "".join(re.findall('name=\"csrfToken\" value=\"(.*?)\">',response.text))
         
    def teardown_class(self):
        ClassDB.init_db()
    
    def test_valid_username(self):
        """TC1: 测试有效的账号(5-20位字母或数字)"""
        print("TC1")
        data = {
            'username': "validUser123",
            'password': hash_password("Valid123!"),
            'phone': "13812345671",  
            'email': "test1@example.com",
        }
        response = self.session.post(self.REGISTER_URL, data=data)
        self.assertEqual(response.status_code, 200)
        self.assertIn("showerror(\"1\")",response.text)
        
    def test_username_too_short(self):
        """TC2: 测试账号长度不足(少于5位)"""
        print("TC2")
        data = {
            "username": "usr2",
            "password": hash_password("Valid123!"),
            "phone": "13812345672",
            "email": "test2@example.com"
        }
        response = self.session.post(self.REGISTER_URL, data=data)
        self.assertEqual(response.status_code, 200)
        self.assertIn("账号必须是5-20位字母或数字", response.text)
        
    def test_username_too_long(self):
        """TC3: 测试账号长度超出(超过20位)"""
        print("TC3")  
        data = {
            "username": "user12345678901234567890",
            "password": hash_password("Valid123!"),
            "phone": "13812345673",
            "email": "test3@example.com"
        }
        response = self.session.post(self.REGISTER_URL, data=data)
        self.assertEqual(response.status_code, 200)
        self.assertIn("账号必须是5-20位字母或数字", response.text)
         
    def test_valid_password_registration(self):
        """TC4: 输入符合要求的密码,预期结果:注册成功"""
        print("TC4")
        data = {
            "username": "validUser4",
            "password": hash_password("ValidPassword123!"),
            "phone": "13812345674",
            "email": "valid4@example.com"
        }
        response = self.session.post(self.REGISTER_URL, data=data)
        self.assertEqual(response.status_code, 200)
        self.assertIn("showerror(\"1\")", response.text)  
         
         
    def test_valid_mobile_number(self):
        """TC8: 输入有效的中国手机号码,预期结果:注册成功"""
        print("TC8")
        data = {
            "username": "validUser8",
            "password": hash_password("ValidPassword123!"),
            "phone": "13812345677",
            "email": "valid8@example.com"
        }
        response = self.session.post(self.REGISTER_URL, data=data)
        self.assertEqual(response.status_code, 200)
        self.assertIn("showerror(\"1\")", response.text)
         
    def test_invalid_mobile_number_format(self):
        """TC9: 输入非法的手机号码格式,预期结果:显示错误信息'手机号格式错误'"""
        print("TC9")
        data = {
            "username": "invalidPhoneUser9",
            "password": hash_password("ValidPassword123!"),
            "phone": "12345",  # 故意输入错误格式的手机号  
            "email": "invalid9@example.com"
        }
        response = self.session.post(self.REGISTER_URL, data=data)
        self.assertEqual(response.status_code, 200)
        self.assertIn("手机号必须符合中国手机号码格式", response.text)
         
    def test_valid_email(self):
        """TC10: 输入有效的邮箱地址,预期结果:注册成功"""
        print("TC10")
        data = {
            "username": "newUser10",
            "password": hash_password("SecurePassword123!"),
            "phone": "13912345678",
            "email": "use10r@example.com"
        }
        response = self.session.post(self.REGISTER_URL, data=data)
        self.assertEqual(response.status_code, 200)
        self.assertIn("showerror(\"1\")", response.text)
         
    def test_invalid_email_format(self):
        """TC11: 输入非法的邮箱格式,预期结果:显示错误信息'邮箱格式错误'"""
        print("TC11")  
        data = {
            "username": "userWithBadEmail",
            "password": hash_password("AnotherSecure123!"),
            "phone": "13987654321",
            "email": "bademail.com"  # 故意输入错误格式的邮箱
        }
        response = self.session.post(self.REGISTER_URL, data=data)
        self.assertEqual(response.status_code, 200)
        self.assertIn("Email格式不正确", response.text)
         
         
         
    def test_duplicate_username_registration(self):
        print("TC16")
        """TC16: 同名用户注册测试"""
        data = {
            "username": "testUser",
            "password": hash_password("Password123!"),
            "phone": "13912345679",
            "email": "test@example.com"
        }
        # 第一次注册  
        response1 = self.session.post(self.REGISTER_URL,data=data)
        # 第二次注册
        response2 = self.session.post(self.REGISTER_URL,data=data)
        self.assertIn("showerror(\"username\")",response2.text)
         
    def test_duplicate_email_registration(self):
        """TC17: 重复邮箱注册测试"""
        print("TC17")
        data1 = {
            "username": "user1",
            "password": hash_password("Password123!"),
            "phone": "13912345680",
            "email": "jerry@example.com"
        }
        data2 = {
            "username": "user2",
            "password": hash_password("Password123!"),
            "phone": "13912345681",
            "email": "jerry@example.com"
        }
        # 第一次注册
        response = self.session.post(self.REGISTER_URL,data=data1)  
        # 第二次注册
        response = self.session.post(self.REGISTER_URL,data=data2)
        self.assertIn("showerror(\"email\")",response.text)
         
    def test_duplicate_phone_registration(self):
        """TC18: 重复手机号注册测试"""
        print("TC18")
        data1 = {
            "username": "user3",
            "password": hash_password("Password123!"),
            "phone": "13912345682",
            "email": "user3@example.com"
        }
        data2 = {
            "username": "user4",
            "password": hash_password("Password123!"),
            "phone": "13912345682",
            "email": "user4@example.com"
        }
        # 第一次注册
        self.session.post(self.REGISTER_URL,data=data1)
        # 第二次注册  
        response = self.session.post(self.REGISTER_URL,data=data2)
        self.assertIn("showerror(\"phone\")",response.text)
         
    
if __name__ == "__main__":
unittest.main()

Test_register_GUI.py

代码语言:javascript

复制

import pytest
from playwright.sync_api import Page
from Util import ClassDB
         
REGISTER_URL = 'http://127.0.0.1:8080/CharGPTEbusiness/register.html'
# 测试类
class TestUserRegistration:   
    def teardown_class(self):
        ClassDB.init_db()
         
    def test_valid_username(self,page: Page):
        page.goto(REGISTER_URL)
        page.fill('input[name="username"]', 'validUser123')
        page.fill('input[name="password"]', 'ValidPass123!')
        page.fill('input[name="confirmPassword"]', 'ValidPass123!')  
        page.fill('input[name="phone"]', '13800138000')
        page.fill('input[name="email"]', 'test@example.com')
        page.click('button[type="submit"]')
        # 检查注册是否成功(这里需要根据实际页面反馈调整)
        assert page.locator('text=注册成功').is_visible()
         
    def test_password_length_error(self,page: Page):
        page.goto(REGISTER_URL)
        page.fill('input[name="username"]', 'testUser')
        page.fill('input[name="password"]', 'NoSpecialChar1')
        page.fill('input[name="confirmPassword"]', 'NoSpecialChar1')
        page.fill('input[name="phone"]', '13800138000')
        page.fill('input[name="email"]', 'test@example.com')
        page.click('button[type="submit"]')
        # 检查错误信息
        assert page.locator('text=密码必须包含大小写字母、数字和特殊字符').is_visible()
         
    def test_invalid_phone_format(self,page: Page):
        page.goto(REGISTER_URL)
        page.fill('input[name="username"]', 'testUser')
        page.fill('input[name="password"]', 'ValidPass123!')  
        page.fill('input[name="confirmPassword"]', 'ValidPass123!')
        page.fill('input[name="phone"]', '12345')  # 非法手机号
        page.fill('input[name="email"]', 'test@example.com')
        page.click('button[type="submit"]')
        # 检查错误信息
        assert page.locator('text=请输入有效的中国手机号').is_visible()
         
         
    def test_duplicate_registration(self,page: Page):
        page.goto(REGISTER_URL)
        # 第一次注册
        page.fill('input[name="username"]', 'duplicateUser')
        page.fill('input[name="password"]', 'ValidPass123!')
        page.fill('input[name="confirmPassword"]', 'ValidPass123!')
        page.fill('input[name="phone"]', '13800138001')
        page.fill('input[name="email"]', 'duplicate@example.com')
        page.click('button[type="submit"]')
         
        # 检查注册成功
        assert page.locator('text=注册成功').is_visible()
         
        # 第二次注册相同用户  
        page.goto(REGISTER_URL)
        page.fill('input[name="username"]', 'duplicateUser')
        page.fill('input[name="password"]', 'ValidPass123!')
        page.fill('input[name="confirmPassword"]', 'ValidPass123!')
        page.fill('input[name="phone"]', '13800138001')
        page.fill('input[name="email"]', 'duplicate@example.com')
        page.click('button[type="submit"]')
        # 检查错误信息
        assert page.locator('text=用户名已经被注册过!').is_visible()
         
if __name__ == '__main__':
    pytest.main(["-sv", "Test_register_GUI.py"])

6.2.2测试代码评审回复

你的测试代码结构清晰,功能覆盖全面,能够有效测试用户注册功能。以下是对 TestRegister.py 和 Test_register_GUI.py 的评审和改进建议:

1. TestRegister.py

优点:

结构清晰:使用 unittest 框架,测试用例分明,易于理解。

覆盖全面:涵盖了有效注册、无效输入、重复用户等多种场景。

使用了会话:通过 requests.Session() 维护会话状态,适合于需要保持登录状态的测试。

改进建议:

teardown_class 方法命名:根据 unittest 的命名约定,建议将 teardown_class 改为 tearDownClass,以便于识别和遵循 Python 的命名规范。(改为def tearDown(self))

错误信息检查:在检查错误信息时,建议使用更具体的断言,例如 self.assertIn("手机号格式错误", response.text),以确保捕获到的错误信息是准确的。

使用常量:将 URL 和其他常量提取为类属性或常量,以便于维护和修改。

测试用例命名:可以考虑更清晰的命名,例如 test_username_length_too_short,以便于快速理解每个测试用例的意图。(修改)

数据库状态管理:在每个测试后重置数据库状态,以确保测试之间不会相互影响。

2. Test_register_GUI.py

优点:

使用 Playwright:通过 Playwright 进行 GUI 测试,能够模拟用户操作,适合前端功能测试。

覆盖全面:涵盖了有效注册、密码长度错误、手机号格式错误等场景。

改进建议:

错误信息检查:在检查错误信息时,可以考虑使用 assert page.locator('text=...').is_visible() 来确保错误信息的可见性,增强测试的可靠性。

页面加载等待:在进行页面交互之前,可以考虑添加等待逻辑,以确保页面完全加载。这可以通过 page.wait_for_load_state('networkidle') 或者其他适合的方法实现。

测试用例命名:同样,建议使用更具描述性的测试用例名称,例如 test_valid_username_registration,以便于快速理解每个测试用例的目的。

代码重复:在多个测试用例中可能会重复使用相同的输入数据,建议将这些数据提取到类属性或方法中,以减少重复代码。

总体建议

使用 Mock 或 Fixture:在进行数据库操作时,可以考虑使用 Mock 或 Fixture,以避免对实际数据库的依赖,确保测试的独立性和稳定性。

测试报告:可以考虑集成测试报告工具,记录测试结果,便于后续分析和改进。

代码风格:确保遵循 PEP 8 风格指南,保持代码的一致性和可读性。

总体来说,你的测试代码已经相当不错,通过以上的改进建议,可以进一步提升测试的稳定性和可维护性。希望这些建议对你有所帮助!

目录
相关文章
|
17天前
|
供应链 监控 安全
对话|企业如何构建更完善的容器供应链安全防护体系
阿里云与企业共筑容器供应链安全
171341 14
|
20天前
|
供应链 监控 安全
对话|企业如何构建更完善的容器供应链安全防护体系
随着云计算和DevOps的兴起,容器技术和自动化在软件开发中扮演着愈发重要的角色,但也带来了新的安全挑战。阿里云针对这些挑战,组织了一场关于云上安全的深度访谈,邀请了内部专家穆寰、匡大虎和黄竹刚,深入探讨了容器安全与软件供应链安全的关系,分析了当前的安全隐患及应对策略,并介绍了阿里云提供的安全解决方案,包括容器镜像服务ACR、容器服务ACK、网格服务ASM等,旨在帮助企业构建涵盖整个软件开发生命周期的安全防护体系。通过加强基础设施安全性、技术创新以及倡导协同安全理念,阿里云致力于与客户共同建设更加安全可靠的软件供应链环境。
150297 32
|
28天前
|
弹性计算 人工智能 安全
对话 | ECS如何构筑企业上云的第一道安全防线
随着中小企业加速上云,数据泄露、网络攻击等安全威胁日益严重。阿里云推出深度访谈栏目,汇聚产品技术专家,探讨云上安全问题及应对策略。首期节目聚焦ECS安全性,提出三道防线:数据安全、网络安全和身份认证与权限管理,确保用户在云端的数据主权和业务稳定。此外,阿里云还推出了“ECS 99套餐”,以高性价比提供全面的安全保障,帮助中小企业安全上云。
201972 15
对话 | ECS如何构筑企业上云的第一道安全防线
|
6天前
|
机器学习/深度学习 自然语言处理 PyTorch
深入剖析Transformer架构中的多头注意力机制
多头注意力机制(Multi-Head Attention)是Transformer模型中的核心组件,通过并行运行多个独立的注意力机制,捕捉输入序列中不同子空间的语义关联。每个“头”独立处理Query、Key和Value矩阵,经过缩放点积注意力运算后,所有头的输出被拼接并通过线性层融合,最终生成更全面的表示。多头注意力不仅增强了模型对复杂依赖关系的理解,还在自然语言处理任务如机器翻译和阅读理解中表现出色。通过多头自注意力机制,模型在同一序列内部进行多角度的注意力计算,进一步提升了表达能力和泛化性能。
|
10天前
|
存储 人工智能 安全
对话|无影如何助力企业构建办公安全防护体系
阿里云无影助力企业构建办公安全防护体系
1256 11
|
12天前
|
机器学习/深度学习 自然语言处理 搜索推荐
自注意力机制全解析:从原理到计算细节,一文尽览!
自注意力机制(Self-Attention)最早可追溯至20世纪70年代的神经网络研究,但直到2017年Google Brain团队提出Transformer架构后才广泛应用于深度学习。它通过计算序列内部元素间的相关性,捕捉复杂依赖关系,并支持并行化训练,显著提升了处理长文本和序列数据的能力。相比传统的RNN、LSTM和GRU,自注意力机制在自然语言处理(NLP)、计算机视觉、语音识别及推荐系统等领域展现出卓越性能。其核心步骤包括生成查询(Q)、键(K)和值(V)向量,计算缩放点积注意力得分,应用Softmax归一化,以及加权求和生成输出。自注意力机制提高了模型的表达能力,带来了更精准的服务。
|
11天前
|
人工智能 自然语言处理 程序员
通义灵码2.0全新升级,AI程序员全面开放使用
通义灵码2.0来了,成为全球首个同时上线JetBrains和VSCode的AI 程序员产品!立即下载更新最新插件使用。
1416 25
|
10天前
|
消息中间件 人工智能 运维
1月更文特别场——寻找用云高手,分享云&AI实践
我们寻找你,用云高手,欢迎分享你的真知灼见!
813 38
1月更文特别场——寻找用云高手,分享云&AI实践
|
1天前
|
存储 人工智能 分布式计算
湖仓实时化升级 :Uniflow 构建流批一体实时湖仓
本文整理自阿里云产品经理李昊哲在Flink Forward Asia 2024流批一体专场的分享,涵盖实时湖仓发展趋势、基于Flink搭建流批一体实时湖仓及Materialized Table优化三方面。首先探讨了实时湖仓的发展趋势和背景,特别是阿里云在该领域的领导地位。接着介绍了Uniflow解决方案,通过Flink CDC、Paimon存储等技术实现低成本、高性能的流批一体处理。最后,重点讲解了Materialized Table如何简化用户操作,提升数据查询和补数体验,助力企业高效应对不同业务需求。
318 17
湖仓实时化升级 :Uniflow 构建流批一体实时湖仓
|
16天前
|
人工智能 自然语言处理 API
阿里云百炼xWaytoAGI共学课DAY1 - 必须了解的企业级AI应用开发知识点
本课程旨在介绍阿里云百炼大模型平台的核心功能和应用场景,帮助开发者和技术小白快速上手,体验AI的强大能力,并探索企业级AI应用开发的可能性。