10.1生成测试用例
产品登录页面如下:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="../css/index.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></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>
<title>登录页面</title>
<script src="../js/index.js" type="text/javascript"></script>
</head>
<body>
<div class="container">
<h2>登录</h2>
<form id="loginForm" name="loginForm" method="POST" action="login.jsp" onsubmit="return validateLoginForm();">
<input type="hidden" id="csrftoken" name="csrftoken" value="vRB6kNVanUDbT8g5b1bMY3fx6eoHXJhHiStsDc8EyAdTx2BkHrFCIXavHSA2ss6u6eZUMYLvH4pJmxCAGcpWWUguwme7AXy6NkTk">
<div>
<label for="username">用户名:</label>
<input type="text" id="username" name="username" placeholder="输入账号" required>
<div id="usernameError" class="error"></div>
</div>
<div>
<label for="password">密码:</label>
<input type="password" id="password" name="password" placeholder="输入密码" required>
<div id="passwordError" class="error"></div>
</div>
<button type="submit">登录</button>
</form>
<div id="loginError" class="error">
</div>
</div>
</body>
</html>
登录页面界面包括。
l账号:文本框,5-20位大小写英文字符、数字。重复的账户不允许注册
l密码:密码框,必须包括大小写英文字符、数字和特殊字符,5-30位,SHA256散列传输。
l登录成功出现“登录XXXX系统”提示。
l登录失败,出现“用户名或密码错误“。
l避免XSS注入
l避免CSRF注入
l避免SQL注入
l不登录,直接在浏览器地址栏粘贴登录后地址,返回登录页面。
请设计测试用例。
以下是针对产品登录页面的测试用例设计,涵盖了功能性、边界情况和安全性等方面的测试:
测试用例设计
用例ID | 测试内容 | 输入数据 | 预期结果 |
---|---|---|---|
TC-001 | 验证有效用户名和密码登录 | 用户名: validUser | 登录成功,显示“登录XXXX系统”提示 |
TC-002 | 验证无效用户名登录 | 用户名: invalidUser | 显示“用户名或密码错误” |
TC-003 | 验证无效密码登录 | 用户名: validUser, 密码: wrongPassword | 显示“用户名或密码错误” |
TC-004 | 验证空用户名登录 | 用户名: ""密码: validPassword | 显示“用户名或密码错误” |
TC-005 | 验证空密码登录 | 用户名: validUser密码: "" | 显示“用户名或密码错误” |
TC-006 | 验证用户名长度边界 | 用户名: "a"(1个字符) | 显示“用户名长度不符合要求” |
TC-007 | 验证密码长度边界 | 密码: "123"(3个字符) | 显示“密码长度不符合要求” |
用例ID | 测试内容 | 输入数据 | 预期结果 |
---|---|---|---|
TC-008 | 验证用户名最大长度 | 用户名: "validUser123456789012345" | 登录成功,显示“登录XXXX系统”提示 |
TC-009 | 验证用户名最小长度 | 用户名: "user" | 登录成功,显示“登录XXXX系统”提示 |
TC-010 | 验证密码最大长度 | 密码: "ValidPassword123!"(30个字符) | 登录成功,显示“登录XXXX系统”提示 |
TC-011 | 验证密码最小长度 | 密码: "P@ss1" | 登录成功,显示“登录XXXX系统”提示 |
用例ID | 测试内容 | 输入数据 | 预期结果 |
---|---|---|---|
TC-012 | 验证XSS注入 | 用户名: " <> alert('XSS') " | 显示“用户名或密码错误” |
TC-013 | 验证CSRF保护 | 模拟CSRF攻击 | 登录失败,显示“用户名或密码错误” |
TC-014 | 验证SQL注入 | 用户名: "admin' --" | 显示“用户名或密码错误” |
用例ID | 测试内容 | 输入数据 | 预期结果 |
---|---|---|---|
TC-015 | 验证未登录用户访问登录后地址 | 直接访问: "/dashboard" | 重定向回登录页面 |
备注
l测试用例中涉及的“有效用户名”和“有效密码”需要根据系统的实际注册情况进行定义。
l安全性测试用例需要在安全测试环境中进行,以避免对生产环境造成影响。
l测试用例的执行结果应记录实际结果与预期结果的对比,确保功能的正确性和安全性。
假设存在一个账户,用户名:validUser,密码:ValidPassword123!。
用例ID | 测试内容 | 输入数据 | 预期结果 |
---|---|---|---|
TC-001 | 验证有效用户名和密码登录 | 用户名: validUser密码:ValidPassword123! | 登录成功,显示“登录XXXX系统”提示 |
TC-002 | 验证无效用户名登录 | 用户名: invalidUser密码:ValidPassword123! | 显示“用户名或密码错误” |
TC-003 | 验证无效密码登录 | 用户名: validUser, 密码: wrongPassword | 显示“用户名或密码错误” |
TC-004 | 验证空用户名登录 | 用户名: ""密码: validPassword | 显示“账号必须是5-20位字母或数字” |
TC-005 | 验证空密码登录 | 用户名: validUser密码: "" | 显示“用户名或密码错误” |
TC-006 | 验证用户名长度边界 | 用户名: "a"(1个字符) | 显示“账号必须是5-20位字母或数字” |
TC-007 | 验证密码长度边界 | 密码: "123"(3个字符) | 显示“密码必须包含大小写字母、数字和特殊字符,长度在5-30之间” |
lTC-001、TC-002、TC-003、TC-004、TC-005、TC-005:用API测试用例实现。
lTC-006、TC-007:用GUI测试用例实现。
用例ID | 测试内容 | 输入数据 | 预期结果 |
---|---|---|---|
TC-008 | 验证用户名最大长度 | 用户名: "validUser12345678900" | 登录成功,显示“登录XXXX系统”提示 |
TC-009 | 验证用户名最小长度 | 用户名: "user1" | 登录成功,显示“登录XXXX系统”提示 |
TC-010 | 验证密码最大长度 | 密码: "ValidPassword1234567890123456!"(30个字符) | 登录成功,显示“登录XXXX系统”提示 |
TC-011 | 验证密码最小长度 | 密码: "P@ss1" | 登录成功,显示“登录XXXX系统”提示 |
所以测试用例均用API测试用例实现。
用例ID | 测试内容 | 输入数据 | 预期结果 |
---|---|---|---|
TC-012 | 验证XSS注入 | 用户名: " <> alert('XSS') " | 显示“账号必须是5-20位字母或数字” |
TC-013 | 验证CSRF保护 | 模拟CSRF攻击 | 登录失败,显示“ ” |
TC-014 | 验证SQL注入 | 用户名: "admin' --" | 显示“账号必须是5-20位字母或数字” |
所以测试用例均用API测试用例实现。
用例ID | 测试内容 | 输入数据 | 预期结果 |
---|---|---|---|
TC-015 | 验证未登录用户访问登录后地址 | 登录,然后登出,直接访问: "/welcome.jsp" | 重定向回登录页面 |
所以测试用例均用GUI测试用例实现。
生成如下的基于Python requests类+unittest框架的API测试用例脚本。
lTC-001,验证有效用户名和密码登录,用户名: validUser,密码:ValidPassword123!,登录成功,显示“登录XXXX系统”提示。
lTC-002,验证无效用户名登录,用户名: invalidUser,密码:ValidPassword123!,显示“用户名或密码错误”。
lTC-003,验证无效密码登录,用户名: validUser, 密码: wrongPassword,显示“用户名或密码错误”。
lTC-004,验证空用户名登录,用户名: "",密码: validPassword ,显示“账号必须是5-20位字母或数字”。
lTC-005,验证空密码登录,用户名: validUser,密码: "",显示“密码应该哈希进行存储”。
lTC-008,验证用户名最大长度,用户名: "validUser123456789012345",登录成功,显示“登录XXXX系统”提示。
lTC-009,验证用户名最小长度,用户名: "user1",登录成功,显示“登录XXXX系统”提示。
lTC-010,验证密码最大长度,密码: "ValidPassword1234567890123456!"(30个字符),登录成功,显示“登录XXXX系统”提示。
lTC-011,验证密码最小长度,密码: "P@ss1",登录成功,显示“登录XXXX系统”提示。
lTC-012,验证XSS注入,用户名: " <> alert('XSS') " ,显示“用户名或密码错误”。
lTC-013,验证CSRF保护,模拟CSRF攻击,登录失败,显示“用户名或密码错误”。
lTC-014,验证SQL注入,用户名: "admin' --" ,显示“用户名或密码错误”。
下面是一个基于Python requests 类和 unittest 框架的API测试用例脚本,涵盖您提供的所有测试用例。请确保在运行此脚本之前,您已安装了 requests 库,并将相应的URL和响应内容替换为您实际使用的API。
import requests
import unittest
# 基础URL
BASE_URL = "http://your-api-url.com/login" # 替换为实际的登录API URL
WELCOME_URL = "http://your-api-url.com/welcome.jsp" # 替换为实际的受保护页面URL
class TestLoginAPI(unittest.TestCase):
def test_valid_login(self):
response = requests.post(BASE_URL, data={'username': "validUser", 'password': "ValidPassword123!"})
self.assertEqual(response.json().get('message'), "登录XXXX系统")
def test_invalid_username(self):
response = requests.post(BASE_URL, data={'username': "invalidUser", 'password': "ValidPassword123!"})
self.assertEqual(response.json().get('message'), "用户名或密码错误")
def test_invalid_password(self):
response = requests.post(BASE_URL, data={'username': "validUser", 'password': "wrongPassword"})
self.assertEqual(response.json().get('message'), "用户名或密码错误")
def test_empty_username(self):
response = requests.post(BASE_URL, data={'username': "", 'password': "validPassword"})
self.assertEqual(response.json().get('message'), "账号必须是5-20位字母或数字")
def test_empty_password(self):
response = requests.post(BASE_URL, data={'username': "validUser", 'password': ""})
self.assertEqual(response.json().get('message'), "密码应该哈希进行存储")
def test_username_max_length(self):
response = requests.post(BASE_URL, data={'username': "validUser123456789012345", 'password': "ValidPassword123!"})
self.assertEqual(response.json().get('message'), "登录XXXX系统")
def test_username_min_length(self):
response = requests.post(BASE_URL, data={'username': "user1", 'password': "ValidPassword123!"})
self.assertEqual(response.json().get('message'), "登录XXXX系统")
def test_password_max_length(self):
response = requests.post(BASE_URL, data={'username': "validUser", 'password': "ValidPassword1234567890123456!"})
self.assertEqual(response.json().get('message'), "登录XXXX系统")
def test_password_min_length(self):
response = requests.post(BASE_URL, data={'username': "validUser", 'password': "P@ss1"})
self.assertEqual(response.json().get('message'), "登录XXXX系统")
def test_xss_injection(self):
response = requests.post(BASE_URL, data={'username': " <> alert('XSS') ", 'password': "ValidPassword123!"})
self.assertEqual(response.json().get('message'), "用户名或密码错误")
def test_csrf_protection(self):
# 假设CSRF令牌是必需的,这里我们不提供CSRF令牌
response = requests.post(BASE_URL, data={'username': "validUser", 'password': "ValidPassword123!"})
self.assertEqual(response.json().get('message'), "用户名或密码错误")
def test_sql_injection(self):
response = requests.post(BASE_URL, data={'username': "admin' --", 'password': "ValidPassword123!"})
self.assertEqual(response.json().get('message'), "用户名或密码错误")
if __name__ == "__main__":
unittest.main()
10.2.3调整登录API测试程序
import pymysql
import re
import hashlib
class ClassDB:
#初始化数据库连接
def initDB(self):
host = 'localhost'
user = 'root'
password = '123456'
database = 'ebusiness'
# 创建数据库连接
connection = pymysql.connect(host=host, user=user, password=password, database=database)
return connection
# 关闭数据库连接
def closedb(self,connection):
connection.close()
# 清空数据库user表中数据,用于DearDown
def init_db(self):
connection = self.initDB()
try:
with connection.cursor() as cursor:
# SQL语句,用于清空user表
sql = "TRUNCATE TABLE user"
# 执行SQL语句
cursor.execute(sql)
# 提交事务
connection.commit()
finally:
self.closedb(connection)
#为登录初始化数据
def init_login(self):
connection = self.initDB()
try:
with connection.cursor() as cursor:
password = HASH.hash_password("ValidPassword123!")
# SQL语句,用于清空user表
sql = "INSERT INTO user (username, password, phone, email) VALUES (\"validUser\",\""+password+"\", \"13611223344\", \"a@b.com\")"
# 执行SQL语句
cursor.execute(sql)
# 提交事务
connection.commit()
finally:
self.closedb(connection)
#建立特定用户名的user数据,密码为默认“ValidPassword123!”
def login_for_name(self,username):
connection = self.initDB()
try:
with connection.cursor() as cursor:
password = HASH.hash_password("ValidPassword123!")
# SQL语句,用于清空user表
sql = "INSERT INTO user (username, password, phone, email) VALUES (\""+username+"\",\""+password+"\", \"13611223344\", \"a@b.com\")"
# 执行SQL语句
cursor.execute(sql)
# 提交事务
connection.commit()
finally:
self.closedb(connection)
#建立特定密码的user数据,用户名为默认“validUser”
def login_for_password(self,password):
connection = self.initDB()
try:
with connection.cursor() as cursor:
password = HASH.hash_password(password)
# SQL语句,用于清空user表
sql = "INSERT INTO user (username, password, phone, email) VALUES (\"validUser\",\""+password+"\", \"13611223344\", \"a@b.com\")"
# 执行SQL语句
cursor.execute(sql)
# 提交事务
connection.commit()
finally:
self.closedb(connection)
class CSRF:
def get_csrf(html):
pattern = r'input[^>]+value="([^"]+)"'
# 使用 re.search 查找匹配
match = re.search(pattern, html)
# 提取并打印 value 值
if match:
csrftoken_value = match.group(1)
return csrftoken_value
else:
return "未找到 csrftoken"
class HASH:
#对字符串仅更新SHA256散列
def hash_password(password):
"""对密码进行SHA256散列"""
return hashlib.sha256(password.encode('utf-8')).hexdigest()
2)修改TestEbusiness.py为Test_Register.py
…
from Util import ClassDB
from Util import CSRF
from Util import HASH
class TestUserRegistration(unittest.TestCase):
# 注册接口的URL
def setUp(self):
self.REGISTER_URL = "http://127.0.0.1:8080/CharGPTEbusiness/jsp/register.jsp"
self.session = requests.Session()
response = self.session.get(self.REGISTER_URL)
self.csrf = CSRF.get_csrf(response.text)
self.token={"csrftoken":self.csrf}
def tearDown(self):
self.db = ClassDB()
self.db.init_db()
…
3) 修改Test_register_GUI.py
…
from Util import ClassDB
import time
REGISTER_URL = 'http://127.0.0.1:8080/CharGPTEbusiness/jsp/register.jsp'
# 测试类
class TestUserRegistration:
def teardown_class(self):
self.db = ClassDB()
self.db.init_db()
…
4)建立Test_Login.py
import requests
import unittest
from Util import ClassDB
from Util import CSRF
from Util import HASH
# 基础URL
BASE_URL = "http://127.0.0.1:8080/CharGPTEbusiness/jsp/login.jsp" # 替换为实际的登录API URL
WELCOME_URL = "http://127.0.0.1:8080/CharGPTEbusiness/jsp//welcome.jsp" # 替换为实际的受保护页面URL
class TestLoginAPI(unittest.TestCase):
def setUp(self):
self.session = requests.Session()
response = self.session.get(BASE_URL)
self.csrf = CSRF.get_csrf(response.text)
self.token={"csrftoken":self.csrf}
self.db = ClassDB()
self.db.init_login()
def tearDown(self):
self.db.init_db()
def test_valid_login(self):
"""TC-001:验证有效用户名和密码登录"""
self.token['username'] = "validUser"
response = requests.post(BASE_URL, data={
'csrftoken':self.csrf,
'username': "validUser",
'password': HASH.hash_password("ValidPassword123!")
},cookies=self.token)
self.assertEqual(response.status_code, 200)
self.assertIn("系统欢迎您", response.text)
def test_invalid_username(self):
"""TC-002:验证无效用户名登录"""
self.token['username'] = "invalidUser"
response = requests.post(BASE_URL,
data={
'csrftoken':self.csrf,
'username': "invalidUser",
'password': HASH.hash_password("ValidPassword123!")
},cookies=self.token)
self.assertEqual(response.status_code, 200)
self.assertIn("用户名或密码错误", response.text)
def test_invalid_password(self):
"""TC-003,验证无效密码登录"""
self.token['username'] = "validUser"
response = requests.post(BASE_URL, data={
'csrftoken':self.csrf,
'username': "validUser",
'password': HASH.hash_password("wrongPassword")
},cookies=self.token)
self.assertEqual(response.status_code, 200)
self.assertIn("用户名或密码错误", response.text)
def test_empty_username(self):
"""TC-004,验证空用户名登录"""
self.token['username'] = ""
response = requests.post(BASE_URL, data={
'csrftoken':self.csrf,
'username': "",
'password':HASH. hash_password("validPassword")
},cookies=self.token)
self.assertEqual(response.status_code, 200)
self.assertIn(" ", response.text)
def test_empty_password(self):
"""TC-005,验证空密码登录"""
self.token['username'] = "validUser"
response = requests.post(BASE_URL, data={
'csrftoken':self.csrf,
'username': "validUser",
'password': HASH.hash_password("")
},cookies=self.token)
self.assertEqual(response.status_code, 200)
self.assertIn("用户名或密码错误", response.text)
def test_username_max_length(self):
"""TC-008,验证用户名最大长度"""
self.token['username'] = "validUser12345678900"
self.db.login_for_name("validUser12345678900")
response = requests.post(BASE_URL, data={
'csrftoken':self.csrf,
'username': "validUser12345678900",
'password': HASH.hash_password("ValidPassword123!")
},cookies=self.token)
self.assertEqual(response.status_code, 200)
self.assertIn("系统欢迎您", response.text)
def test_username_min_length(self):
"""TC-009,验证用户名最小长度"""
self.token['username'] = "user1"
self.db.login_for_name("user1")
response = requests.post(BASE_URL, data={
'csrftoken':self.csrf,
'username': "user1",
'password':HASH.hash_password("ValidPassword123!")
},cookies=self.token)
self.assertEqual(response.status_code, 200)
self.assertIn("欢迎", response.text)
self.assertIn("登录", response.text)
self.assertIn("系统", response.text)
def test_password_max_length(self):
"""TC-010,验证密码最大长度"""
self.token['username'] = "validUser"
self.db.login_for_password("ValidPassword1234567890123456!")
response = requests.post(BASE_URL, data={
'csrftoken':self.csrf,
'username': "validUser",
'password': HASH.hash_password("ValidPassword1234567890123456!")
},cookies=self.token)
self.assertEqual(response.status_code, 200)
self.assertIn("系统欢迎您", response.text)
def test_password_min_length(self):
"""TC-011,验证密码最小长度"""
self.token['username'] = "validUser"
self.db.login_for_password("P@ss1")
response = requests.post(BASE_URL, data={
'csrftoken':self.csrf,
'username': "validUser",
'password': HASH.hash_password("P@ss1")
},cookies=self.token)
self.assertEqual(response.status_code, 200)
self.assertIn("系统欢迎您", response.text)
def test_xss_injection(self):
"""TC-012,验证XSS注入"""
self.token['username'] = " <> alert('XSS') "
response = requests.post(BASE_URL, data={
'csrftoken':self.csrf,
'username': " <> alert('XSS1') ",
'password': HASH.hash_password("ValidPassword123!")
},cookies=self.token)
self.assertEqual(response.status_code, 200)
self.assertIn("账号必须是5-20位字母或数字", response.text)
def test_csrf_protection(self):
"""TC-013,验证CSRF保护"""
# 假设CSRF令牌是必需的,这里我们不提供CSRF令牌
self.token['username'] = "validUser"
response = requests.post(BASE_URL, data={
'username': "validUser",
'password': HASH.hash_password("ValidPassword123!")
},cookies=self.token)
self.assertEqual(response.status_code, 200)
self.assertIn(" ", response.text)
def test_sql_injection(self):
"""TC-014,验证SQL注入"""
self.token['username'] = "admin' --"
response = requests.post(BASE_URL, data={
'csrftoken':self.csrf,
'username': "admin' --",
'password': HASH.hash_password("ValidPassword123!")
},cookies=self.token)
self.assertEqual(response.status_code, 200)
self.assertIn("账号必须是5-20位字母或数字", response.text)
if __name__ == "__main__":
unittest.main()
10.3 生成登录GUI测试用例
10.3.1 申请生成登录GUI测试用例
对下面用例书写基于playwright+pytest的测试脚本。
前端代码
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="../css/index.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></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>
<title>登录页面</title>
<script src="../js/index.js" type="text/javascript"></script>
</head>
<body>
<div class="container">
<h2>登录</h2>
<form id="loginForm" name="loginForm" method="POST" action="login.jsp" onsubmit="return validateLoginForm();">
<input type="hidden" id="csrftoken" name="csrftoken" value="yDhOJU4vOCv6qSENtpba8xaURQlQlpGQ9W4hJekAt45dLi9ByMEs2DOzaLwtjeo2P4b1pV0crpDqxQo1F3w23blijWE1OBsj99ke">
<div>
<label for="username">用户名:</label>
<input type="text" id="username" name="username" placeholder="输入账号" required>
<div id="usernameError" class="error"></div>
</div>
<div>
<label for="password">密码:</label>
<input type="password" id="password" name="password" placeholder="输入密码" required>
<div id="passwordError" class="error"></div>
</div>
<button type="submit">登录</button>
</form>
<div id="loginError" class="error">
</div>
</div>
</body>
</html>
对下面用例实现基于GUI的测试用例
lTC-001 验证有效用户名和密码登录,用户名: validUser,登录成功,显示“登录XXXX系统”提示。
lTC-002 验证无效用户名登录,用户名: invalidUser,显示“用户名或密码错误”。
lTC-006 验证用户名长度边界,用户名: "a"(1个字符),显示“账号必须是5-20位字母或数字”。
lTC-007 验证密码长度边界,密码: "123"(3个字符),显示“密码必须包含大小写字母、数字和特殊字符,长度在5-30之间”。
lTC-015 验证未登录用户访问登录后地址,登录,然后登出,直接访问: "/welcome.jsp"重定向回登录页面。
假设存在一个账户,用户名:validUser,密码:ValidPassword123!。
下面是基于Playwright和pytest的测试脚本实现,涵盖了您提供的用例。请确保您已经安装了Playwright和pytest,并且已正确配置了测试环境。
首先,您需要在项目目录中创建一个新的Python文件,比如test_login.py,然后将以下代码粘贴到该文件中:
import pytest
from playwright.sync_api import sync_playwright
# 测试用例
@pytest.mark.parametrize("username, password, expected_message", [
("validUser", "ValidPassword123!", "登录XXXX系统"), # TC-001
("invalidUser", "ValidPassword123!", "用户名或密码错误"), # TC-002
("a", "ValidPassword123!", "账号必须是5-20位字母或数字"), # TC-006
("validUser", "123", "密码必须包含大小写字母、数字和特殊字符,长度在5-30之间"), # TC-007
])
def test_login(username, password, expected_message):
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
page.goto("file://path/to/your/html/file.html") # 替换为您的HTML文件路径
# 填充用户名和密码
page.fill("#username", username)
page.fill("#password", password)
# 提交表单
page.click("button[type='submit']")
# 等待页面加载并验证消息
page.wait_for_selector("#loginError, h2") # 等待登录错误或成功消息
if expected_message == "登录XXXX系统":
assert page.locator("h2").inner_text() == expected_message
else:
assert page.locator("#loginError").inner_text() == expected_message
browser.close()
def test_access_protected_page_after_logout():
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
page = browser.new_page()
# 登录
page.goto("file://path/to/your/html/file.html") # 替换为您的HTML文件路径
page.fill("#username", "validUser")
page.fill("#password", "ValidPassword123!")
page.click("button[type='submit']")
# 退出登录
# 假设有一个退出登录的按钮,您需要根据实际情况进行调整
page.click("#logoutButton") # 替换为实际的注销按钮选择器
# 尝试访问受保护的页面
page.goto("file:///path/to/welcome.jsp") # 替换为欢迎页面的真实路径
# 验证重定向回登录页面
assert page.url == "file://path/to/your/html/file.html" # 替换为登录页面的真实路径
browser.close()
说明
l测试用例: 使用@pytest.mark.parametrize装饰器来定义多个测试用例,涵盖有效和无效的用户名和密码。
l浏览器操作: 使用Playwright的API来启动浏览器、打开页面、填写表单、提交表单并验证结果。
l重定向测试: test_access_protected_page_after_logout函数用于测试未登录用户访问受保护页面的情况。
l路径替换: 请确保将file://path/to/your/html/file.html和file:///path/to/welcome.jsp替换为您本地的实际文件路径。
运行测试
确保您已安装pytest和Playwright。您可以通过以下命令运行测试:
pytest test_login.py
请根据您的具体需求和环境进行调整。
import pytest
from playwright.sync_api import Page
from Util import ClassDB
import time
LOGIN_URL = 'http://127.0.0.1:8080/CharGPTEbusiness/jsp/login.jsp'
WELCOME_URL ='http://127.0.0.1:8080/CharGPTEbusiness/jsp/welcome.jsp'
# 测试类
class TestUserLogin:
def setup_class(self):
self.db = ClassDB()
self.db.init_login()
def teardown_class(self):
self.db.init_db()
# 测试用例
@pytest.mark.parametrize("username, password, expected_message", [
("validUser", "ValidPassword123!", "系统欢迎您"), # TC-001
("invalidUser", "ValidPassword123!", "用户名或密码错误"), # TC-002
("a", "ValidPassword123!", "账号必须是5-20位的字母或数字"), # TC-006
("validUser", "123", "密码必须包含大小写字母、数字和特殊字符,长度在5-30之间"), # TC-007
])
def test_login(self,page: Page,username, password, expected_message):
page.goto(LOGIN_URL)
page.reload()
# 填充用户名和密码
page.fill("#username", username)
page.fill("#password", password)
page.click("button[type='submit']")
# 等待页面加载并验证消息
if expected_message == "系统欢迎您":
page.wait_for_selector("h1") # 等待成功消息
else:
page.wait_for_selector("h2") # 等待登录错误
if username == "validUser":
if password != "123":
assert page.locator("h1").inner_text() == expected_message
else:
assert expected_message in page.locator("#passwordError").inner_text()
elif username == "invalidUser":
assert expected_message in page.locator("#loginError").inner_text()
elif username == "a":
assert expected_message in page.locator("#usernameError").inner_text()
else:
assert 0==1
def test_access_protected_page_after_logout(self,page: Page):
# 登录
page.goto(LOGIN_URL)
page.reload()
page.fill("#username", "validUser")
page.fill("#password", "ValidPassword123!")
page.click("button[type='submit']")
page.wait_for_selector("h1") # 等待成功消息
page.click("body > a") # 替换为实际的注销按钮选择器
# 尝试访问受保护的页面
page.goto(WELCOME_URL) # 替换为欢迎页面的真实路径
# 验证重定向回登录页面
assert page.url == LOGIN_URL # 替换为登录页面的真实路径
if __name__ == '__main__':
pytest.main(["-sv", "Test_login_GUI.py"])
在这里注意
由于expected_messagez在页面中不同的地方出现,所以调整代码:
if username == "validUser":
if password != "123":
assert page.locator("h1").inner_text() == expected_message
else:
assert expected_message in page.locator("#passwordError").inner_text()
elif username == "invalidUser":
assert expected_message in page.locator("#loginError").inner_text()
elif username == "a":
assert expected_message in page.locator("#usernameError").inner_text()
else:
assert 0==1
另外在运行过程中发现:
page.fill("#username", "…")
page.fill("#password", "…")
page.click("button[type='submit']")
经常等待不到,后来通过playwright的录制功能,换了
page.get_by_placeholder("输入账号").fill("validUser")
page.get_by_placeholder("输入密码").fill("ValidPassword123!")
page.get_by_role("button", name="登录").click()
就没有问题了。
注意:在使用ChatGPT辅助开发代码的时候,注意结合其他工具一起使用 |
---|
另外在运行过程中发现每次密码没有执行散列就传输了,后来发现前端代码
< src="https://cdn.jsdelivr.net/npm/crypto-js@4.1.1/sha256.js">
由于网速很慢,https://cdn.jsdelivr.net/npm/crypto-js@4.1.1/sha256.js无法加载到。把core.js、crypto-js.min.jsh和sha256.js放在项目js目录下。register.jsp和login.js改为:
< src="../js/crypto-js.min.js">
< src="../js/core.js">
< src="../js/sha256.js">
根据登录GUI测试程序调整注册GUI测试。使用pytest.mark.parametrizec参数模式。
注意:ChatGPT每次给出的格式不一致,需要根据经验使用最好的程序 |
---|
import pytest
from playwright.sync_api import Page
from Util import ClassDB
import time
REGISTER_URL = 'http://127.0.0.1:8080/CharGPTEbusiness/jsp/register.jsp'
# 测试类
class TestUserRegistration:
def teardown(self):
self.db = ClassDB()
self.db.init_db()
# 测试用例
@pytest.mark.parametrize("username, password, repassword, phone, email, expected_message", [
("validUser123", "ValidPass123!", "ValidPass123!","13800138000","test@example.com","登录"), #GTC1-账号有效性测试
("testUser", "Aa1@", "Aa1@","13800138000","test@example.com","密码必须包含大小写字母、数字和特殊字符,长度在5-30之间"), #GTC2 - 密码长度不足测试
("testUser", "iLongpassword12@Longpassword12@", "iLongpassword12@Longpassword12@","13800138000","test@example.com","密码必须包含大小写字母、数字和特殊字符,长度在5-30之间"), #GTC3 - 密码长度超出测试
("testUser", "ValidPass123!", "validPass123!","13800138000","test@example.com","密码确认不一致"), #GTC4 - 输入密码与确认密码确保不一致性
("testUser", "ValidPass123!", "ValidPass123!","12345","test@example.com","请输入有效的中国手机号"), ##GTC5 - 手机号格式错误测试
])
def test_Login(self,page: Page,username, password, repassword, phone, email, expected_message):
page.goto(REGISTER_URL)
page.reload()
page.fill('input[name="username"]', username)
page.fill('input[name="password"]', password)
page.fill('input[name="confirmPassword"]', repassword)
page.fill('input[name="phone"]', phone)
page.fill('input[name="email"]', email)
page.click('button[type="submit"]')
# 检查注册是否成功(这里需要根据实际页面反馈调整)
if expected_message == "登录":
assert page.get_by_role("heading", name="登录")
else:
assert page.locator('text='+expected_message).is_visible()
# 测试用例
@pytest.mark.parametrize("username, password, repassword, phone, email, username_1,phone_1,email_1,expected_message", [
("duplicateUser", "ValidPass123!", "ValidPass123!","13800138000","duplicate@example.com","duplicateUser","13800138001","duplicate2@example.com","注册用户的用户名必须唯一"), #GTC6 – 一个用户重复注册两次。
("duplicateUser3", "ValidPass123!", "ValidPass123!","13800138003","duplicate1@example.com","duplicateUser4","13800138003","duplicate1@example.com","注册用户的手机必须唯一"), #GTC7 – – 不同用户注册,手机号重复
("duplicateUser1", "ValidPass123!", "ValidPass123!","13800138004","duplicate3@example.com","duplicateUser4","13800138005","duplicate3@example.com","注册用户的邮箱必须唯一"), #GTC8 – 不同用户注册,邮件重复
])
def test_duplicate(self,page: Page,username, password, repassword, phone, email, username_1,phone_1,email_1,expected_message):
page.goto(REGISTER_URL)
page.reload()
# 第一次注册
page.fill('input[name="username"]', username)
page.fill('input[name="password"]', password)
page.fill('input[name="confirmPassword"]', repassword)
page.fill('input[name="phone"]', phone)
page.fill('input[name="email"]', email)
page.click('button[type="submit"]')
# 检查注册成功
assert page.get_by_role("heading", name="登录")
# 第二次注册相同用户
page.goto(REGISTER_URL)
page.reload()
page.fill('input[name="username"]', username_1)
page.fill('input[name="password"]', password)
page.fill('input[name="confirmPassword"]', repassword)
page.fill('input[name="phone"]', phone_1)
page.fill('input[name="email"]', email_1)
page.click('button[type="submit"]')
# 检查错误信息
print(expected_message)
assert page.locator('text='+expected_message).is_visible()
if __name__ == '__main__':
pytest.main(["-sv", "Test_register_GUI.py"])
共有以下测试用例,其中API测试用例12个,分别如下:
lTC-001 验证有效用户名和密码登录
lTC-002 验证无效用户名登录
lTC-003 验证无效密码登录
lTC-004 验证空用户名登录
lTC-005 验证空密码登录
lTC-008 验证用户名最大长度
lTC-009 验证用户名最小长度
lTC-010 验证密码最大长度
lTC-011 验证密码最小长度
lTC-012 验证XSS注入
lTC-013 验证CSRF保护
lTC-014 验证SQL注入
GUI测试用例5个,分别如下:
lTC-001 验证有效用户名和密码登录
lTC-002 验证无效用户名登录
lTC-006 验证用户名长度边界
lTC-007 验证密码长度边界
lTC-015 验证未登录用户访问登录后地址
没有手工测试用例。