一、项目介绍
项目背景
商城购物系统立足于数字经济时代背景,是零售业数字化转型的典型实践。随着互联网基础设施的完善和移动支付的普及,线上购物已成为社会主流消费方式。该项目旨在构建一个连接商家与消费者的高效线上平台,其核心意义在于突破传统零售的时空与地域限制,为中小型企业提供低成本、广覆盖的销售渠道,大幅降低运营成本。对消费者而言,则能享受到无与伦比的便捷性、丰富的商品选择及个性化的购物体验。
测试计划
本次测试将全面验证商城系统的核心功能,包括商品展示、用户认证及交易交互等关键环节,确保其运行符合需求规格,从而为用户提供完整、正确且流畅的核心购物体验。
我将重点评估系统在预期用户负载下的性能表现,通过监测关键指标如响应时间和稳定性,来确保系统能够满足用户的基本使用体验,具备可靠的业务承载能力。
我们计划通过实施自动化测试来覆盖核心高频场景,以此在后续的系统迭代中高效保障主要功能的稳定性,并显著降低回归测试的周期与成本。
二、项目功能及用例设计
登录界面
用户登录功能
该项目提供三种登录方式:账号登录,手机号登录,邮箱登录
测试用例设计如下:
找回密码功能
测试用例设计如下:
用户注册功能
该项目提供三种注册方式:账号注册,手机号注册,邮箱注册
测试用例设计如下:
商城首页
商城首页有两种访问方式:游客访问、用户访问。这两种方式对于浏览商品都是有权限访问的,但是对于商品交互功能,如收藏、购买,是不允许游客模式进行操作的,必须进行登录才可进行后续操作。
首页展示的功能包括:个人信息说明、顶部导航区、图片轮换、商品分类、商品详情信息展示等信息。
首页交互功能包括:搜索功能、顶部导航区交互、商品详情页点击、商品详细类型点击等。
测试用例设计如下:
顶部导航区交互
顶部导航区的核心交互聚焦于用户个人信息的便捷查看与高效管理(主要功能包括:个人中心、收藏夹、购物车等)。个人中心作为关键入口,为用户提供了集中访问和维护其账户资料、订单历史及隐私设置的流畅体验,收藏夹和购物车查询更适合放在商品详情页中配合,在商品收藏后查看是否收藏成功,在加入购物车后查看是否添加成功。
个人中心中主要包含:头像、昵称的显示、头像的修改功能、个人资料的修改功能、地址的修改功能等功能。
测试用例设计如下:
商品搜索交互
搜索交互主要功能为:搜索出的符合要求的商品,商品数量显示正确;提供价格不同区间筛选功能;销量排序、热度排序、价格排序、时间排序功能
测试用例设计如下:
商品分区交互
分区交互主要功能为:搜索出的符合该分类的商品,商品数量显示正确;提供价格不同区间筛选功能;销量排序、热度排序、价格排序、时间排序功能;大分类中包含多个小分类等
经过测试,该项目的分区筛选功能是有问题的。
测试用例设计如下:
商品详情交互
商品详情页主要功能为:数量选择、收藏、加入购物车、直接购买、商品详细介绍、商品评论等。
测试用例设计如下:
其他方面测试
测试用例设计如下:
三、自动化测试脚本编写
测试框架:使用 Selenium WebDriver 进行 Web 页面的自动化测试,模拟用户在浏览器中的各种操作。
WebDriver 管理工具:使用 WebDriverManager 来自动管理浏览器驱动,避免手动下载和配置浏览器驱动的繁琐过程。
Utils.py
Driver类是一个基于 Selenium 的 Chrome 浏览器驱动封装类
特点:
- 使用单例模式:通过ShoppingDriver=Driver()创建了一个实例,供多个测试文件共享使用
- 自动管理驱动:使用webdriver_manager自动安装和管理 ChromeDriver
- 包含截图功能:提供了便捷的页面截图方法
- 简单的初始化配置:设置了 3 秒的隐式等待时间
主要作用:
1. 封装浏览器驱动:简化 Chrome 浏览器的初始化过程,隐藏了复杂的配置细节。
2. 提供页面截图能力:
自动按日期创建图片文件夹(在项目根目录的 images 文件夹下)。
截图文件名包含调用方法名和精确时间,便于追溯。
3. 统一资源管理:通过单例模式确保多个测试文件共享同一个浏览器实例,节省资源。
import datetime
import os
import sys
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
class Driver:
driver = ""
def __init__(self):
options = webdriver.ChromeOptions()
# options.add_argument("-headless") #无头模式
# options.page_load_strategy = 'eager' # 更改页面加载策略,加快访问速度,但是有可能会导致没有等待到元素加载出来就获取
self.driver = webdriver.Chrome(service = Service(ChromeDriverManager().install()),options = options)
self.driver.implicitly_wait(3)
def getScreeShot(self):
#先创建文件
dirname = datetime.datetime.now().strftime('%Y-%m-%d')
#如果没有找到文件夹就生成一个,不是在common文件夹生成文件夹,而是在总目录下生成一个带日期的文件夹
if not os.path.exists("../images/"+dirname):
os.mkdir("../images/"+dirname)
filename = sys._getframe().f_back.f_code.co_name+"-"+datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S")
self.driver.save_screenshot("../images/"+dirname+"/"+filename+".png")
#为了更好定位到照片信息,获取调用的方法名加进名字里
#sys._getframe().f_back.f_code.co_name
ShoppingDriver=Driver()#四个测试文件都只用一个实例出来的对象
ShoppingRegister.py
ShoppingRegister类: 注册功能自动化测试类
主要特点:
- 依赖共享驱动:使用ShoppingDriver单例对象的浏览器驱动,实现测试资源共享
- 集成验证码识别:结合pytesseract和PIL库实现验证码图片的自动识别
- 测试场景完整:包含注册成功和多种注册失败场景的测试用例
- 错误处理机制:发生异常时自动截图保存,便于问题排查
主要作用:
1. 注册功能自动化测试:
提供RegisterSuccess方法测试正常注册流程
提供RegisterFail方法测试各种异常注册场景(如用户名过短/过长、密码不符合要求等)
2. 验证码自动处理:
通过getverifycode方法自动截取验证码图片
对图片进行预处理并使用 OCR 技术识别数字验证码
3. 测试结果验证:
注册成功后验证首页 logo 元素
注册失败时验证是否停留在注册页面
import time
from io import BytesIO
import pytesseract
from PIL import Image
from selenium.webdriver import Keys
from selenium.webdriver.common.by import By
from common.Utils import ShoppingDriver
# 配置Tesseract路径(根据实际情况修改)
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
#用户名、密码、验证码、同意协议的选择器
username="body > div.body-content-container > div.body-content-formal-container > div.am-g.user-register-container.theme-data-edit-event > div > div > div.am-radius-lg.am-background-white > div > div.am-tabs-bd.am-border-0 > div.am-tab-panel.am-active > form > div:nth-child(1) > input"
password="body > div.body-content-container > div.body-content-formal-container > div.am-g.user-register-container.theme-data-edit-event > div > div > div.am-radius-lg.am-background-white > div > div.am-tabs-bd.am-border-0 > div.am-tab-panel.am-active > form > div:nth-child(2) > input"
verify_code="body > div.body-content-container > div.body-content-formal-container > div.am-g.user-register-container.theme-data-edit-event > div > div > div.am-radius-lg.am-background-white > div > div.am-tabs-bd.am-border-0 > div.am-tab-panel.am-active > form > div:nth-child(3) > input"
agree_text="body > div.body-content-container > div.body-content-formal-container > div.am-g.user-register-container.theme-data-edit-event > div > div > div.am-radius-lg.am-background-white > div > div.am-tabs-bd.am-border-0 > div.am-tab-panel.am-active > form > div.agreement.am-margin-top-main > label > span"
class ShoppingRegister:
url=""
driver=""
def __init__(self):
self.driver=ShoppingDriver.driver
self.url="http://8.155.1.153/?s=user/reginfo.html"
self.driver.get(self.url)
self.driver.fullscreen_window()
def getverifycode(self):
try:
# 定位元素
element = self.driver.find_element(By.CSS_SELECTOR, "#form-verify-img")
# 截图验证码的元素(Selenium 自带方法,直接对元素截图,比全图截图精准)
img_bytes = element.screenshot_as_png
img_element = Image.open(BytesIO(img_bytes))
# 用OCR工具识别图片
# 预处理:转为灰度图
img_element = img_element.convert("L")
# OCR 识别(限制识别范围:只识别数字)
img_text = pytesseract.image_to_string(
img_element,
config="--psm 10 -c tessedit_char_whitelist=0123456789"
)
# 清洗结果(去掉空白、换行符)
img_text = img_text.strip()
return img_text
except Exception as e:
print("getverifycode failed")
print(e)
# 添加截图
ShoppingDriver.getScreeShot()
raise
def RegisterSuccess(self):
try:
self.driver.find_element(By.CSS_SELECTOR, username).clear()
self.driver.find_element(By.CSS_SELECTOR, password).clear()
self.driver.find_element(By.CSS_SELECTOR, verify_code).clear()
# 输入框输入账号
self.driver.find_element(By.CSS_SELECTOR, username).send_keys("888888")
# 输入框输入密码
self.driver.find_element(By.CSS_SELECTOR, password).send_keys("bit123321..")
# 输入框输入验证码
# 获取验证码
img_text = ShoppingRegister.getverifycode(self)
# 输入验证码
self.driver.find_element(By.CSS_SELECTOR, verify_code).send_keys(img_text)
# 点击同意协议
(self.driver.find_element(By.CSS_SELECTOR, "body > div.body-content-container > div.body-content-formal-container > div.am-g.user-register-container.theme-data-edit-event > div > div > div.am-radius-lg.am-background-white > div > div.am-tabs-bd.am-border-0 > div.am-tab-panel.am-active > form > div.agreement.am-margin-top-main > label > span").click())
# 点击注册
(self.driver.find_element(By.CSS_SELECTOR,
"body > div.body-content-container > div.body-content-formal-container > div.am-g.user-register-container.theme-data-edit-event > div > div > div.am-radius-lg.am-background-white > div > div.am-tabs-bd.am-border-0 > div.am-tab-panel.am-active > form > div.am-form-group.am-margin-top-main.am-padding-0 > button")
.click())
time.sleep(3)
# 注册成功后,验证网站logo,确认是不是进入了首页
self.driver.find_element(By.CSS_SELECTOR,
"body > div.body-content-container > div.body-content-formal-container > div.nav-seasrch.header-nav-simple.am-hide-sm-only > div > div.logo.theme-data-edit-event > a > img")
except Exception as e:
print("RegisterSuccess failed")
print(e)
# 添加截图
ShoppingDriver.getScreeShot()
raise
def RegisterFail(self):
try:
flag=False
time.sleep(2)
# 获取验证码
img_text = ShoppingRegister.getverifycode(self)
test_dic = [
{"desc": "用户名长度<2字符注册", "username": "a", "password": "123456",
"verify_code": img_text, "agree_text": True},
{"desc": "用户名长度>18字符注册", "username": "a" * 19, "password": "123456",
"verify_code": img_text, "agree_text": True},
{"desc": "不写用户名注册", "username": "", "password": "123456",
"verify_code": img_text, "agree_text": True},
{"desc": "密码长度<6字符注册", "username": "test_user", "password": "123",
"verify_code": img_text, "agree_text": True},
{"desc": "密码长度>18字符注册", "username": "test_user", "password": "a" * 19,
"verify_code": img_text, "agree_text": True},
{"desc": "不写密码注册", "username": "test_user", "password": "",
"verify_code": img_text, "agree_text": True},
{"desc": "不写验证码注册", "username": "test_user", "password": "123456",
"verify_code": "", "agree_text": True},
{"desc": "错误的验证码注册", "username": "test_user", "password": "123456",
"verify_code": "wrong", "agree_text": True},
{"desc": "不点击同意协议注册", "username": "test_user", "password": "123456",
"verify_code": img_text, "agree_text": False}
]
for dic in test_dic:
#print(f"执行测试用例: {dic['desc']}")
# 清空输入框
self.driver.find_element(By.CSS_SELECTOR, username).clear()
self.driver.find_element(By.CSS_SELECTOR, password).clear()
self.driver.find_element(By.CSS_SELECTOR, verify_code).clear()
time.sleep(1)
# 输入用户名
if dic["username"]:
self.driver.find_element(By.CSS_SELECTOR,username).send_keys(dic["username"])
# 输入密码
if dic["password"]:
self.driver.find_element(By.CSS_SELECTOR, password).send_keys(dic["password"])
# 输入验证码
if dic["verify_code"]:
self.driver.find_element(By.CSS_SELECTOR, verify_code).send_keys(dic["verify_code"])
# 处理同意协议
agree_checkbox = self.driver.find_element(By.CSS_SELECTOR, agree_text)
# 第一次if:需要点击同意,同时按钮没有被点击
# 第二次if:需要点击同意,但是按钮已经被点击,所以不需要再点击
if dic["agree_text"] :
if not flag:
agree_checkbox.click()
flag=True
#print("按钮没有被点击,才点的")
#else:
#print("按钮已经点过了")
#最后一次if:不需要点击,同时按钮已经被点击,那就再点击一下取消
elif not dic["agree_text"] :
if flag:
agree_checkbox.click()
#print("取消按钮")
# 点击注册
(self.driver.find_element(By.CSS_SELECTOR,
"body > div.body-content-container > div.body-content-formal-container > div.am-g.user-register-container.theme-data-edit-event > div > div > div.am-radius-lg.am-background-white > div > div.am-tabs-bd.am-border-0 > div.am-tab-panel.am-active > form > div.am-form-group.am-margin-top-main.am-padding-0 > button")
.click())
# 注册失败后,验证是不是还在注册界面
self.driver.find_element(By.CSS_SELECTOR,"body > div.body-content-container > div.body-content-formal-container > div.nav-seasrch.header-nav-simple > div > p.login-title.am-fl.am-vertical-align-middle.am-margin-left-main")
except Exception as e:
print("RegisterFail failed")
print(e)
# 添加截图
ShoppingDriver.getScreeShot()
raise
ShoppingLogin.py
ShoppingLogin类与之前的ShoppingRegister类在设计思路上高度协同,专门用于覆盖登录场景的正常与异常测试。
主要作用:
1. 自动化执行登录功能测试,提升测试效率
替代人工操作,自动完成 “打开登录页→输入信息→识别验证码→点击登录→验证结果” 全流程。
2. 覆盖登录场景的异常边界,保障功能稳定性
LoginFail方法覆盖 10 种典型异常场景,包括:
用户名边界:长度 <2、>18、为空、错误用户名;
密码边界:长度 <6、>18、为空、错误密码;
验证码边界:为空、错误验证码;
确保系统对非法输入有正确的拦截逻辑(如不跳转首页、停留在登录页)。
3. 提供可追溯的测试结果,辅助问题排查
异常时自动保存截图(含调用方法名 + 时间戳),结合打印的错误日志,可快速定位 “哪一步失败”“失败时页面状态”,降低问题排查成本。
4. 与注册类协同,构成用户认证流程测试闭环
与ShoppingRegister类共享ShoppingDriver和验证码识别逻辑,共同覆盖 “注册→登录” 的核心用户流程,可组合执行以验证完整的用户认证功能(如注册新用户后,用该账号执行LoginSuccess测试)。
import time
from io import BytesIO
from time import sleep
import pytesseract
from PIL import Image
from selenium.webdriver.common.by import By
from common.Utils import ShoppingDriver
# 配置Tesseract路径(根据实际情况修改)
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
username="body > div.body-content-container > div.body-content-formal-container > div.am-g.user-login-container.am-padding-top-0.am-padding-bottom-0.theme-data-edit-event > div > div.am-container > div.am-u-sm-12.am-u-md-6.am-u-lg-4.user-right-container > div.am-radius-lg.am-background-white > div > div.am-tabs-bd.am-border-0 > div.am-tab-panel.am-active > form > div:nth-child(1) > input"
password="body > div.body-content-container > div.body-content-formal-container > div.am-g.user-login-container.am-padding-top-0.am-padding-bottom-0.theme-data-edit-event > div > div.am-container > div.am-u-sm-12.am-u-md-6.am-u-lg-4.user-right-container > div.am-radius-lg.am-background-white > div > div.am-tabs-bd.am-border-0 > div.am-tab-panel.am-active > form > div:nth-child(2) > input"
verify_code="body > div.body-content-container > div.body-content-formal-container > div.am-g.user-login-container.am-padding-top-0.am-padding-bottom-0.theme-data-edit-event > div > div.am-container > div.am-u-sm-12.am-u-md-6.am-u-lg-4.user-right-container > div.am-radius-lg.am-background-white > div > div.am-tabs-bd.am-border-0 > div.am-tab-panel.am-active > form > div:nth-child(3) > input"
class ShoppingLogin:
url=""
driver=""
def __init__(self):
self.driver=ShoppingDriver.driver
self.url="http://8.155.1.153/?s=user/logininfo.html"
self.driver.get(self.url)
self.driver.fullscreen_window()
def getverifycode(self):
try:
# 定位元素
element = self.driver.find_element(By.CSS_SELECTOR, "#form-verify-img")
# 截图验证码的元素(Selenium 自带方法,直接对元素截图,比全图截图精准)
img_bytes = element.screenshot_as_png
img_element = Image.open(BytesIO(img_bytes))
# 用OCR工具识别图片
# 预处理:转为灰度图
img_element = img_element.convert("L")
# OCR 识别(限制识别范围:只识别数字)
img_text = pytesseract.image_to_string(
img_element,
config="--psm 10 -c tessedit_char_whitelist=0123456789"
)
# 清洗结果(去掉空白、换行符)
img_text = img_text.strip()
return img_text
except Exception as e:
print("getverifycode failed")
print(e)
# 添加截图
ShoppingDriver.getScreeShot()
raise
def LoginSuccess(self):
time.sleep(1)
try:
self.driver.find_element(By.CSS_SELECTOR, username).clear()
self.driver.find_element(By.CSS_SELECTOR, password).clear()
self.driver.find_element(By.CSS_SELECTOR, verify_code).clear()
#输入框输入账号
self.driver.find_element(By.CSS_SELECTOR, username).send_keys("dz000")
#输入框输入密码
self.driver.find_element(By.CSS_SELECTOR, password).send_keys("bit12344321..")
#输入框输入验证码
# 获取验证码
img_text = ShoppingLogin.getverifycode(self)
# 输入验证码
self.driver.find_element(By.CSS_SELECTOR, verify_code).send_keys(img_text)
#点击登录
self.driver.find_element(By.CSS_SELECTOR, "body > div.body-content-container > div.body-content-formal-container > div.am-g.user-login-container.am-padding-top-0.am-padding-bottom-0.theme-data-edit-event > div > div.am-container > div.am-u-sm-12.am-u-md-6.am-u-lg-4.user-right-container > div.am-radius-lg.am-background-white > div > div.am-tabs-bd.am-border-0 > div.am-tab-panel.am-active > form > div.am-form-group.am-form-group-refreshing.am-margin-top-lg.am-padding-0 > button").click()
#进入首页后,验证网站logo,确认是不是进入了首页
self.driver.find_element(By.CSS_SELECTOR, "body > div.body-content-container > div.body-content-formal-container > div.nav-seasrch.header-nav-simple.am-hide-sm-only > div > div.logo.theme-data-edit-event > a > img")
except Exception as e:
print("LoginSuccess failed")
print(e)
#添加截图
ShoppingDriver.getScreeShot()
raise
def LoginFail(self):
try:
time.sleep(1)
# 获取验证码
img_text = ShoppingLogin.getverifycode(self)
test_dic = [
{"desc": "用户名长度<2字符登录", "username": "a", "password": "bit12344321..",
"verify_code": img_text},
{"desc": "用户名长度>18字符登录", "username": "a" * 19, "password": "bit12344321..",
"verify_code": img_text},
{"desc": "不写用户名登录", "username": "", "password": "bit12344321..",
"verify_code": img_text},
{"desc": "错误的用户名登录", "username": "dz669", "password": "bit12344321..",
"verify_code": img_text},
{"desc": "密码长度<6字符登录", "username": "dz000", "password": "123",
"verify_code": img_text},
{"desc": "密码长度>18字符登录", "username": "dz000", "password": "a" * 19,
"verify_code": img_text},
{"desc": "不写密码登录", "username": "dz000", "password": "",
"verify_code": img_text},
{"desc": "错误的密码登录", "username": "dz000", "password": "bit1234321..",
"verify_code": img_text},
{"desc": "不写验证码登录", "username": "dz000", "password": "bit12344321..",
"verify_code": ""},
{"desc": "错误的验证码登录", "username": "dz000", "password": "bit12344321..",
"verify_code": "wrong"},
]
for dic in test_dic:
#print(f"执行测试用例: {dic['desc']}")
# 清空输入框
self.driver.find_element(By.CSS_SELECTOR, username).clear()
self.driver.find_element(By.CSS_SELECTOR, password).clear()
self.driver.find_element(By.CSS_SELECTOR, verify_code).clear()
time.sleep(1)
# 输入用户名
if dic["username"]:
self.driver.find_element(By.CSS_SELECTOR, username).send_keys(dic["username"])
# 输入密码
if dic["password"]:
self.driver.find_element(By.CSS_SELECTOR, password).send_keys(dic["password"])
# 输入验证码
if dic["verify_code"]:
self.driver.find_element(By.CSS_SELECTOR, verify_code).send_keys(dic["verify_code"])
# 点击登录
self.driver.find_element(By.CSS_SELECTOR,
"body > div.body-content-container > div.body-content-formal-container > div.am-g.user-login-container.am-padding-top-0.am-padding-bottom-0.theme-data-edit-event > div > div.am-container > div.am-u-sm-12.am-u-md-6.am-u-lg-4.user-right-container > div.am-radius-lg.am-background-white > div > div.am-tabs-bd.am-border-0 > div.am-tab-panel.am-active > form > div.am-form-group.am-form-group-refreshing.am-margin-top-lg.am-padding-0 > button").click()
# 登录失败后,验证网站logo,确认是不是还在登录界面
self.driver.find_element(By.CSS_SELECTOR,"body > div.body-content-container > div.body-content-formal-container > div.nav-seasrch.header-nav-simple > div > p.login-title.am-fl.am-vertical-align-middle.am-margin-left-main")
except Exception as e:
print("LoginFail failed")
print(e)
# 添加截图
ShoppingDriver.getScreeShot()
raise
RunTest.py
按顺序定义的顺序串联执行各类测试场景,形成从 “注册→登录→首页交互” 的完整测试链路实现对商城系统核心功能的自动化验证
资源管理
- 复用ShoppingDriver单例浏览器对象,确保所有测试在同一浏览器实例中执行,避免重复初始化
- 通过finally块保证证测试结束后自动关闭浏览器(driver.quit()),释放资源
from tests import ShoppingLogin
from tests import ShoppingRegister
from tests import ShoppingHome
from common.Utils import ShoppingDriver
if __name__ == "__main__":
try:
print("测试开始")
ShoppingRegister.ShoppingRegister().RegisterFail() # 注册失败
ShoppingHome.ShoppingHome().ShoppingHomeNotLogin() # 未登录状态下的首页
#ShoppingRegister.ShoppingRegister().RegisterSuccess() # 注册成功
ShoppingLogin.ShoppingLogin().LoginSuccess()# 登录成功
ShoppingHome.ShoppingHome().ShoppingHomeCheckByLogin() #登录状态下的首页验证
ShoppingHome.ShoppingHome().Handle_person() # 首页的个人中心交互
ShoppingHome.ShoppingHome().Handle_goods() # 首页的商品交互
print("测试结束")
except:
print("测试失败")
finally:
#测试完成后退出浏览器
ShoppingDriver.driver.quit()
四、测试结论
本次项目测试通过,项目开发耗时XX天,项目测试耗时四天;遗留问题方面,由于用户需求变更,需要添加AI相关功能,推迟到下一个版本进行统一测试;低优先级bug,由于开发测试时间比较紧急,需推迟到下一个版本进行修复。