1.项目背景
本项目是基于 SSM 框架(Spring、Spring MVC、MyBatis) 开发的 论坛系统,包括 用户注册、登录、帖子发布、编辑、删除、查看 等功能,同时提供 帖子列表页和详情页 供用户浏览论坛内容。
用户登录后可以 查看自己和其他用户的帖子,并通过系统 记录帖子发布时间、标题、作者信息。论坛支持 用户评论、私信交流、帖子点赞等社交互动功能,提升用户体验。
为了确保系统的稳定性和功能完整性,项目采用 Selenium 自动化测试 进行功能验证,测试内容涵盖:
-
用户注册与登录:测试不同输入情况下的账号验证逻辑
-
帖子列表展示:确保帖子加载、排序和分页功能正常
-
用户信息校验:验证用户个人资料、修改信息的功能
-
帖子管理:测试帖子发布、编辑、删除是否符合预期
-
帖子详情页:检查帖子的内容是否正确加载,包括评论、点赞等互动功能
-
搜索功能:验证用户输入关键词后,搜索结果是否匹配
本项目通过自动化测试覆盖核心功能,确保论坛系统在不同用户操作和高并发场景下的稳定性和可靠性,同时减少人工测试成本,提高测试效率。
2.测试内容
2.1.功能测试
2.1.1.编写测试用例
2.1.2.执行测试用例
登录测试
- (a)界面展示
- (b)输入正确的账号和密码(如:账号 qiyu,密码 123456)
- 预期结果:成功登录,并跳转至主页。
- 实际结果:正常
- (c)输入错误的账号和密码(如:账号 q,密码 1234)
- 预期结果:登录失败,跳出提示
- 实际结果:正常
- (d)只输入密码(如:密码 123456)
- 预期结果:登录失败,提示密码不可为空
- 实际结果:正常
- (b)只输入账号(如:账号 qiyu)
- 预期结果:登录失败,提示账号不可为空
- 实际结果:正常
- (e)不输入账号和密码
- 预期结果:登录失败,提示账号密码不可为空
- 实际结果:显示正确
个人中心页测试
- (a)界面展示
- (b)测试各各输入框
- 预期结果:能正常输入修改
- 实际结果:头像可上传但无法修改,其他正常
帖子编辑页测试
- (a)界面展示
- (b)测试板块
- 预期结果:默认是java和能正常修改
- 实际结果:正常
- (c)测试标题框和文字框能否正常输入修改
- 预期结果:能否正常输入修改
- 实际结果:修改正常
- (d)测试能否正常发布
- 预期结果:发布成功
- 实际结果:
帖子详细页测试
- (a)界面展示
- (b)测试菜单栏搜索框
- 预期结果:正常输入,正常搜索
- 实际结果:正常输入,但搜索功能无法使用
- (c)测试菜单栏日间夜间切换按钮
- 预期结果:可正常切换
- 实际结果:正常,但会自动切回主页
- (d)测试菜单栏信息按钮
- 预期结果:可弹出消息页
- 实际结果:正常
- (e)测试个人信息
- 预期结果:能与登录用户对应
- 实际结果:对应
- (f)测试点赞按钮
- 预期结果:弹窗提示,点赞数量增加
- 实际结果:成功
- (g)测试评论区
- 预期结果:可发送评论
- 实际结果:正常发送,但在夜间模式输入框输入的文字不可见
- (h)测试发私信按钮
- 预期结果:可弹出回复私信页,可正常发送
- 实际结果:正常
回复私信页测试
- (a)界面展示
- (b)测试输入框
- 预期结果:能正常输入修改
- 实际结果:正常
- (c)测试能否正常发送
- 预期结果:可正常发送
- 实际结果:正常
首页测试
- (a)界面展示
- (b)测试各板块跳转
- 预期结果:可正常跳转
- 实际结果:可正常跳转,但缺失部分板块名称
- (c)测试帖子列表
- 预期结果:点击进入帖子详细页
- 实际结果:正常
我的帖子页测试
- (a)界面展示
- (b)检查页面信息
- 预期结果:能与登录用户对应
- 实际结果:可对应
注册页测试
- (a)界面展示
- (b)正常注册测试
- 预期结果:注册完跳转至登录页
- 实际结果:正常
- (c)异常注册测试
- 预期结果:未输入或两次密码不一致时提示
- 实际结果:正常
2.2 使用 Selenium 进行 Web 自动化测试(Python)
2.2.1 安装 WebDriverManager 和 Selenium
WebDriverManager 是用于管理 Web 驱动程序的工具,在进行 Selenium 测试时,浏览器需要对应的 WebDriver 进行控制和操作。WebDriverManager 能够自动检测本地浏览器版本并下载匹配的驱动程序(如 ChromeDriver、FirefoxDriver 等),避免手动下载和配置的繁琐过程,提高测试效率和稳定性。
2.2.2 创建测试项目
-
在项目中打开 File → Settings,进入设置界面。
-
在“Project: 当前项目名”下,检查是否已安装
selenium
和webdriver-manager
依赖。 -
若未安装,可使用以下命令进行安装:
pip install selenium webdriver-manager
公共工具(Utils.py)
测试目标
-
提供
WebDriver
实例,供各测试脚本调用。 -
统一管理浏览器驱动,支持截图功能。
测试步骤
-
在
Utils.py
中定义BlogDriver
类,初始化Edge
浏览器驱动。 -
提供
get_driver()
方法,返回WebDriver
实例。 -
提供
screenshot()
方法,支持测试过程中截图。
import datetime
import os
import sys
from webdriver_manager.microsoft import EdgeChromiumDriverManager
from selenium import webdriver
from selenium.webdriver.edge.service import Service
class Driver:
driver = None
def __init__(self):
"""初始化WebDriver并启动浏览器"""
options = webdriver.EdgeOptions()
options.page_load_strategy = 'eager'
# options.add_argument('-headless') # 启动无界面模式
edge_driver_path = EdgeChromiumDriverManager().install()
self.driver = webdriver.Edge(service=Service(edge_driver_path), options=options)
def getScreenShot(self):
"""保存当前页面的截图到指定目录"""
dirname = datetime.datetime.now().strftime('%Y-%m-%d')
screenshot_dir = os.path.join(os.path.dirname(os.getcwd()), 'images', dirname)
# 如果截图目录不存在,则创建
if not os.path.exists(screenshot_dir):
os.makedirs(screenshot_dir)
# 根据方法名和时间戳生成唯一的文件名
filename = sys._getframe().f_back.f_code.co_name + "-" + datetime.datetime.now().strftime(
'%Y-%m-%d-%H%M%S') + ".png"
# 保存截图
screenshot_path = os.path.join(screenshot_dir, filename)
self.driver.save_screenshot(screenshot_path)
# 创建BlogDriver实例
BlogDriver = Driver()
登录页自动化测试(Login.py)
测试目标
-
确保用户登录功能正常,包括不同输入组合的处理逻辑。
测试步骤
-
创建
Login.py
,引入Utils.py
及相关依赖。 -
定义
Login
类,管理登录页面的测试用例。 -
实现以下测试方法:
-
正确登录测试:使用正确的用户名和密码进行登录,并验证登录成功。
-
用户名/密码为空测试:测试用户名或密码为空时的登录提示。
-
用户名/密码错误测试:输入错误的用户名或密码,检查是否拒绝登录。
-
页面跳转测试:确保登录成功后跳转到主页。
-
执行方式
-
在
RunTest.py
中调用Login
类执行测试。import time from common.Utils import Driver from selenium.webdriver.common.by import By from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.alert import Alert class Login: url = '' driver = '' def __init__(self): # 初始化URL和driver,打开登录页面 self.url = 'http://127.0.0.1:9580/sign-in.html' self.driver = Driver.driver self.driver.get(self.url) def _login(self, username, password): # 等待提交按钮可点击,输入用户名和密码,并点击登录按钮 WebDriverWait(self.driver,10).until(EC.presence_of_element_located((By.CSS_SELECTOR, '#submit'))) 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, '#username').send_keys(username) self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys(password) self.driver.find_element(By.CSS_SELECTOR,'#submit').click() def _verify_login_success(self): """验证用户是否成功登录""" try: # 等待文章列表标题元素出现来验证是否登录成功 WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#article_list_board_title'))) except: pass # 确认登录后页面中存在文章列表标题 assert self.driver.find_element(By.CSS_SELECTOR, '#article_list_board_title') is not None def login_suc_test1(self): # 测试用户1的登录 self._login('qiyu','123456') self._verify_login_success() Driver.getScreenShot() self.driver.back() def login_suc_test2(self): # 测试用户2的登录 self._login('zhangsan','1234') self._verify_login_success() self.driver.back() def check_error_alert(self): # 检查错误提示是否出现 try: WebDriverWait(self.driver,5).until( EC.presence_of_element_located((By.CSS_SELECTOR,'body > div.jq-toast-wrap.bottom-right > div')) ) except: pass # 验证是否有错误提示 assert self.driver.find_element(By.CSS_SELECTOR,'body > div.jq-toast-wrap.bottom-right > div') def check_error_appear(self, username, password): # 验证错误提示是否在用户名或密码为空时出现 if username == 'empty': try: WebDriverWait(self.driver, 5).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#signInForm > div.mb-3 > div')) ) except: pass # 验证用户名为空时是否出现错误提示 assert self.driver.find_element(By.CSS_SELECTOR, '#signInForm > div.mb-3 > div') if password == 'empty': try: WebDriverWait(self.driver, 5).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#signInForm > div.mb-2 > div > div')) ) except: pass # 验证密码为空时是否出现错误提示 assert self.driver.find_element(By.CSS_SELECTOR, '#signInForm > div.mb-2 > div > div') def test_login_empty_username_empty_password(self): """测试用户名和密码为空时的登录行为""" self._login('', '') self.check_error_appear('empty','empty') self.driver.back() def test_login_empty_username_valid_password(self): """测试用户名为空,密码有效时的登录行为""" self._login('', '1234') self.check_error_appear('empty','') self.driver.back() def test_login_empty_username_invalid_password(self): """测试用户名为空,密码无效时的登录行为""" self._login('', '123') self.check_error_appear('empty','') self.driver.back() def test_login_invalid_username_empty_password(self): """测试用户名无效,密码为空时的登录行为""" self._login('123', '') self.check_error_appear('','empty') self.driver.back() def test_login_valid_username_empty_password(self): """测试用户名有效,密码为空时的登录行为""" self._login('zhangsan', '') self.check_error_appear('','empty') self.driver.back() def test_login_valid_username_invalid_password(self): """测试用户名有效,密码无效时的登录行为""" self._login('zhangsan', '123') self.check_error_alert() self.driver.back() def test_login_invalid_username_valid_password(self): """测试用户名无效,密码有效时的登录行为""" self._login('12312', '12334') self.check_error_alert() self.driver.back() def test_login_invalid_username_invalid_password(self): """测试用户名无效,密码无效时的登录行为""" self._login('123', '123') self.check_error_alert() self.driver.back()
注册页自动化测试(Register.py)
测试目标
-
确保用户注册功能正常,包括不同输入组合的处理逻辑。
测试步骤
-
创建
Register.py
,引入Utils.py
及相关依赖。 -
定义
Register
类,管理注册页面的测试用例。 -
实现以下测试方法:
-
正确注册测试:填写完整有效信息,并检查注册成功后跳转到登录页。
-
用户名/密码/昵称为空测试:测试部分字段为空时,系统是否提示错误。
-
密码不匹配测试:测试密码与确认密码不一致的情况。
-
未勾选用户协议测试:检查未勾选协议时,是否阻止注册。
-
执行方式
-
在
RunTest.py
中调用Register
类执行测试。import time import random from common.Utils import Driver from selenium.webdriver.common.by import By from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class Register: driver = '' url = '' def __init__(self): self.url = 'http://127.0.0.1:9580/sign-up.html' self.driver = Driver.driver self.driver.get(self.url) def test_valid_inputs_agree_terms(self): """测试:所有输入均有效,并且同意条款""" random_name = random.randint(100000, 999999) random_nickname = random.randint(100000, 999999) random_password = random.randint(100000, 999999) # 等待输入框加载 WebDriverWait(self.driver,10).until( EC.presence_of_element_located((By.CSS_SELECTOR,'#username')) ) # 填写表单 self.driver.find_element(By.CSS_SELECTOR, '#username').send_keys(random_name) self.driver.find_element(By.CSS_SELECTOR, '#nickname').send_keys(random_nickname) self.driver.find_element(By.CSS_SELECTOR, '#password').send_keys(random_password) self.driver.find_element(By.CSS_SELECTOR, '#passwordRepeat').send_keys(random_password) self.driver.find_element(By.CSS_SELECTOR, '#policy').click() self.driver.find_element(By.CSS_SELECTOR,'#submit').click() # 等待页面跳转 WebDriverWait(self.driver,10).until( EC.presence_of_element_located((By.CSS_SELECTOR,'body > div > div > div > div:nth-child(1) > div > div.card.card-md > div > h2')) ) # 确认跳转到 "用户登录" 页面 assert self.driver.find_element(By.CSS_SELECTOR,'body > div > div > div > div:nth-child(1) > div > div.card.card-md > div > h2').text == '用户登录' # 登录测试 self.driver.find_element(By.CSS_SELECTOR,'#username').send_keys(random_name) self.driver.find_element(By.CSS_SELECTOR,'#password').send_keys(random_password) self.driver.find_element(By.CSS_SELECTOR,'#submit').click() try: WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR,'#bit-forum-content > div.page-header.d-print-none > div > div > div > h2').text == "首页" ) except: pass assert self.driver.find_element(By.CSS_SELECTOR,'#bit-forum-content > div.page-header.d-print-none > div > div > div > h2').text == '首页' def test_valid_username_empty_others_disagree_terms(self): """测试:用户名有效,其他字段为空,且不同意条款""" WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#username')) ) self.driver.find_element(By.CSS_SELECTOR, '#username').send_keys('random_name') # 点击提交按钮 WebDriverWait(self.driver,10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#submit')) ) self.driver.find_element(By.CSS_SELECTOR, '#submit').click() # 检查错误提示 WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, '#signUpForm > div > div:nth-child(3) > div')) ) WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, '#signUpForm > div > div:nth-child(4) > div')) ) WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, '#signUpForm > div > div:nth-child(5) > div')) ) assert "is-invalid" in self.driver.find_element(By.ID, "policy").get_attribute("class") def test_empty_username_valid_nickname_mixed_passwords_disagree_terms(self): """测试:用户名为空,昵称有效,密码和确认密码不匹配,且不同意条款""" WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#nickname')) ) self.driver.find_element(By.CSS_SELECTOR, '#nickname').send_keys('random_nickname') WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#password')) ) self.driver.find_element(By.CSS_SELECTOR, '#password').send_keys('random_password') WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#passwordRepeat')) ) self.driver.find_element(By.CSS_SELECTOR, '#passwordRepeat').send_keys('random_passwordRepeat') WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#submit')) ) self.driver.find_element(By.CSS_SELECTOR, '#submit').click() # 等待错误信息 WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, '#signUpForm > div > div:nth-child(2) > div')) ) WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, '#signUpForm > div > div:nth-child(5) > div')) ) assert "is-invalid" in self.driver.find_element(By.ID, "policy").get_attribute("class") def test_empty_username_nickname_valid_password_agree_terms(self): """测试:用户名和昵称为空,密码有效,且同意条款""" WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#password')) ) self.driver.find_element(By.CSS_SELECTOR, '#password').send_keys('random_password') WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#passwordRepeat')) ) self.driver.find_element(By.CSS_SELECTOR, '#passwordRepeat').send_keys('random_password') self.driver.find_element(By.CSS_SELECTOR, '#policy').click() WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#submit')) ) self.driver.find_element(By.CSS_SELECTOR, '#submit').click() # 检查是否出现错误信息 WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, '#signUpForm > div > div:nth-child(2) > div')) ) WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, '#signUpForm > div > div:nth-child(3) > div')) ) assert "is-valid" in self.driver.find_element(By.ID, "policy").get_attribute("class") def test_valid_inputs_missing_confirm_password_disagree_terms(self): """测试:所有输入有效,但缺少确认密码,且不同意条款""" WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#username')) ) self.driver.find_element(By.CSS_SELECTOR, '#username').send_keys('random_name') WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#nickname')) ) self.driver.find_element(By.CSS_SELECTOR, '#nickname').send_keys('random_nickname') WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#password')) ) self.driver.find_element(By.CSS_SELECTOR, '#password').send_keys('random_password') WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#submit')) ) self.driver.find_element(By.CSS_SELECTOR, '#submit').click() # 检查是否出现错误信息 WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, '#signUpForm > div > div:nth-child(5) > div')) ) assert "is-invalid" in self.driver.find_element(By.ID, "policy").get_attribute("class") def test_valid_username_empty_nickname_password_confirmed_agree_terms(self): """测试:用户名有效,昵称为空,密码和确认密码匹配,且同意条款""" WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#username')) ) self.driver.find_element(By.CSS_SELECTOR, '#username').send_keys('random_name') WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#password')) ) self.driver.find_element(By.CSS_SELECTOR, '#password').send_keys('random_password') WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#passwordRepeat')) ) self.driver.find_element(By.CSS_SELECTOR, '#passwordRepeat').send_keys('random_password') self.driver.find_element(By.CSS_SELECTOR, '#policy').click() WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#submit')) ) self.driver.find_element(By.CSS_SELECTOR, '#submit').click() # 检查是否出现错误信息 WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, '#signUpForm > div > div:nth-child(3) > div')) ) assert "is-valid" in self.driver.find_element(By.ID, "policy").get_attribute("class") def test_invalid_inputs_disagree_terms(self): """测试:所有输入均无效,并且不同意条款""" WebDriverWait(self.driver,10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#submit')) ) self.driver.find_element(By.CSS_SELECTOR, '#submit').click() # 确保所有字段都显示错误 WebDriverWait(self.driver,10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, '#signUpForm > div > div:nth-child(2) > div')) ) WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, '#signUpForm > div > div:nth-child(3) > div')) ) WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, '#signUpForm > div > div:nth-child(4) > div')) ) WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, '#signUpForm > div > div:nth-child(5) > div')) ) assert "is-invalid" in self.driver.find_element(By.ID, "policy").get_attribute("class")
个人主页自动化测试(PersonalHomepage.py)
测试目标
-
确保个人主页各功能正常,包括修改个人信息、头像、密码等。
测试步骤
-
创建
PersonalHomepage.py
,引入Utils.py
及相关依赖。 -
定义
PersonalHomepage
类,管理个人主页的测试用例。 -
实现以下测试方法:
-
切换页面测试:测试不同版块的切换功能,确保跳转正确。
-
修改头像测试:检查头像上传功能是否正常。
-
修改昵称测试:输入新的昵称并保存,验证是否生效。
-
修改邮箱/手机号测试:更改邮箱或手机号,并检查更新后的信息。
-
修改个人简介测试:输入新简介并保存,检查是否成功。
-
修改密码测试:更改密码并使用新密码重新登录。
-
执行方式
-
在
RunTest.py
中调用PersonalHomepage
类执行测试。from common.Utils import Driver from selenium.webdriver.common.by import By from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from pywinauto import Application from cases import Login class PersonalHomepage: url = '' driver = '' def __init__(self): # 初始化URL和driver,打开首页 self.url = 'http://127.0.0.1:9580/index.html' self.driver = Driver.driver self.driver.get(self.url) # 等待页面加载完毕并确保头像元素可见 WebDriverWait(self.driver,10).until( EC.presence_of_element_located((By.CSS_SELECTOR,'#index_nav_avatar')) ) # 点击头像,进入个人资料设置页面 self.driver.find_element(By.CSS_SELECTOR,'#index_nav_avatar').click() # 等待个人设置按钮可点击,并点击进入 WebDriverWait(self.driver,10).until( EC.presence_of_element_located((By.CSS_SELECTOR,'#index_user_settings')) ) self.driver.find_element(By.CSS_SELECTOR,'#index_user_settings').click() def test_switch_section(self): """测试不同版块的切换功能""" try: # 等待“最新回复”板块加载,确保初始状态正确 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#bit-forum-content > div.page-body > div > div > div:nth-child(2) > h3').text.strip() == "最新回复" ) except: pass # 忽略异常,继续执行 # 断言当前版块为“最新回复” assert self.driver.find_element(By.CSS_SELECTOR, '#bit-forum-content > div.page-body > div > div > div:nth-child(2) > h3').text == "最新回复" # 切换到首页 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#nav_board_index > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#nav_board_index > a > span.nav-link-title').click() try: # 等待首页加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#bit-forum-content > div.page-header.d-print-none > div > div > div > h2').text == "首页" ) except: pass # 忽略异常 # 断言当前版块为“首页” assert self.driver.find_element(By.CSS_SELECTOR, '#bit-forum-content > div.page-header.d-print-none > div > div > div > h2').text == '首页' # 切换到“Java”版块 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#topBoardList > li:nth-child(2) > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#topBoardList > li:nth-child(2) > a > span.nav-link-title').click() try: # 等待“Java”版块加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == "Java" ) except: pass # 断言当前版块为“Java” assert self.driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == 'Java' # 切换到“C++”版块 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#topBoardList > li:nth-child(3) > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#topBoardList > li:nth-child(3) > a > span.nav-link-title').click() try: # 等待“C++”版块加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == "C++" ) except: pass # 断言当前版块为“C++” assert self.driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == 'C++' # 切换到“MySQL”版块 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#topBoardList > li:nth-child(5) > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#topBoardList > li:nth-child(5) > a > span.nav-link-title').click() try: # 等待“MySQL”版块加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == "MySQL" ) except: pass # 断言当前版块为“MySQL” assert self.driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == 'MySQL' def test_search_input(self): """测试搜索框输入功能""" WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.d-none.d-md-flex.me-3 > div > form > div > input')) ) self.driver.find_element(By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.d-none.d-md-flex.me-3 > div > form > div > input').send_keys( 123) # 断言输入的值是否正确 assert self.driver.find_element(By.CSS_SELECTOR, "body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.d-none.d-md-flex.me-3 > div > form > div > input").get_attribute( "value") == '123' def test_toggle_day_night_mode(self): """测试日间/夜间模式切换""" WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-dark > svg')) ) # 断言初始模式为日间模式 assert self.driver.find_element(By.CSS_SELECTOR, 'body').get_attribute("class") == 'theme-light' # 切换到夜间模式 self.driver.find_element(By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-dark > svg').click() WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-light > svg')) ) # 断言模式已切换为夜间模式 assert self.driver.find_element(By.CSS_SELECTOR, 'body').get_attribute("class") == 'theme-dark' # 切换回日间模式 self.driver.find_element(By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-light > svg').click() def test_message_button(self): """测试消息按钮是否可点击并弹出消息面板""" WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > div > a > svg')) ) self.driver.find_element(By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > div > a > svg').click() assert self.driver.find_element(By.CSS_SELECTOR, '#offcanvasEndLabel') def test_user_profile(self): """测试用户个人信息是否正确显示""" self.driver.fullscreen_window() WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#index_nav_nickname')) ) assert self.driver.find_element(By.CSS_SELECTOR, '#index_nav_nickname').text == 'zhangsan' WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#index_nav_name_sub')) ) assert self.driver.find_element(By.CSS_SELECTOR, '#index_nav_name_sub').text == '普通用户' def test_change_avatar(self): # 头像修改测试 # 等待并点击“更换头像”按钮 WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR,'#bit-forum-content > div.page-body > div > div > div > div.col.d-flex.flex-column > div > div.row.align-items-center > div:nth-child(2) > a')) ) self.driver.find_element(By.CSS_SELECTOR,'#bit-forum-content > div.page-body > div > div > div > div.col.d-flex.flex-column > div > div.row.align-items-center > div:nth-child(2) > a').click() # 连接到“打开”窗口 app = Application().connect(title_re='打开', timeout=1) # 连接到 "打开" 窗口 dlg = app.window(title_re="打开") # 获取窗口 assert dlg.exist dlg.close() def test_nickname_field(self): # 昵称栏测试 random_number = random.randint(100, 999) # 等待昵称输入框并进行操作 WebDriverWait(self.driver,10).until( EC.presence_of_element_located((By.CSS_SELECTOR,'#setting_input_nickname')) ) nickname = self.driver.find_element(By.CSS_SELECTOR,'#setting_input_nickname') nickname.clear() nickname.send_keys(random_number) self.driver.find_element(By.CSS_SELECTOR,'#setting_submit_nickname').click() # 检查提示信息是否存在 try: WebDriverWait(self.driver,5).until( EC.presence_of_element_located((By.CSS_SELECTOR,'body > div.jq-toast-wrap.bottom-right > div')) ) except: pass assert self.driver.find_element(By.CSS_SELECTOR,'body > div.jq-toast-wrap.bottom-right > div').text # 恢复为默认的“zhangsan”昵称 nickname.clear() nickname.send_keys('zhangsan') self.driver.find_element(By.CSS_SELECTOR,'#setting_submit_nickname').click() def test_email_field(self): # 邮箱栏测试 random_number = random.randint(100000, 999999) # 等待邮箱输入框并进行操作 WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#setting_input_email')) ) email = self.driver.find_element(By.CSS_SELECTOR, '#setting_input_email') email.clear() email.send_keys(f'{random_number}qq.com') self.driver.find_element(By.CSS_SELECTOR, '#setting_submit_email').click() # 检查提示信息是否存在 try: WebDriverWait(self.driver, 5).until( EC.presence_of_element_located((By.CSS_SELECTOR, 'body > div.jq-toast-wrap.bottom-right > div')) ) except: pass assert self.driver.find_element(By.CSS_SELECTOR, 'body > div.jq-toast-wrap.bottom-right > div') def test_phone_number_field(self): # 电话号码栏测试 random_number = random.randint(10000000000, 99999999999) # 等待并操作电话号码输入框 WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#setting_input_phoneNum')) ) phone = self.driver.find_element(By.CSS_SELECTOR, '#setting_input_phoneNum') phone.clear() phone.send_keys(random_number) self.driver.find_element(By.CSS_SELECTOR, '#setting_submit_phoneNum').click() # 检查提示信息是否存在 try: WebDriverWait(self.driver, 5).until( EC.presence_of_element_located((By.CSS_SELECTOR, 'body > div.jq-toast-wrap.bottom-right > div')) ) except: pass assert self.driver.find_element(By.CSS_SELECTOR, 'body > div.jq-toast-wrap.bottom-right > div') def test_bio_field(self): # 个人简介栏测试 random_number = random.randint(10000000000, 99999999999) # 等待并操作个人简介输入框 WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#settings_textarea_remark')) ) bio = self.driver.find_element(By.CSS_SELECTOR, '#settings_textarea_remark') # 滚动页面使元素可见 self.driver.execute_script("arguments[0].scrollIntoView();", bio) time.sleep(0.5) bio.clear() bio.send_keys(random_number) self.driver.find_element(By.CSS_SELECTOR, '#settings_submit_remark').click() # 检查提示信息是否存在 try: WebDriverWait(self.driver, 5).until( EC.presence_of_element_located((By.CSS_SELECTOR, 'body > div.jq-toast-wrap.bottom-right > div')) ) except: pass assert self.driver.find_element(By.CSS_SELECTOR, 'body > div.jq-toast-wrap.bottom-right > div') def test_change_password(self): # 修改密码栏测试 random_number = random.randint(100, 999) print(f'random_password:{random_number}') # 等待并操作旧密码、新密码、确认新密码输入框 WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#settings_input_oldPassword')) ) oldpassword = self.driver.find_element(By.CSS_SELECTOR, '#settings_input_oldPassword') self.driver.execute_script("arguments[0].scrollIntoView();", oldpassword) oldpassword.clear() oldpassword.send_keys(1234) newpassword = self.driver.find_element(By.CSS_SELECTOR, '#settings_input_newPassword') self.driver.execute_script("arguments[0].scrollIntoView();", newpassword) newpassword.clear() newpassword.send_keys(random_number) cnewpassword = self.driver.find_element(By.CSS_SELECTOR, '#settings_input_passwordRepeat') self.driver.execute_script("arguments[0].scrollIntoView();", cnewpassword) cnewpassword.clear() cnewpassword.send_keys(random_number) element = self.driver.find_element(By.CSS_SELECTOR, '#settings_submit_password') self.driver.execute_script("arguments[0].scrollIntoView();", element) # 提交修改密码 WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#settings_submit_password')) ) time.sleep(0.5) element.click() # 检查密码修改是否成功 try: WebDriverWait(self.driver, 5).until( EC.presence_of_element_located((By.CSS_SELECTOR, 'body > div > div > div > div:nth-child(1) > div > div.card.card-md > div > h2')) ) except: pass assert self.driver.find_element(By.CSS_SELECTOR, 'body > div > div > div > div:nth-child(1) > div > div.card.card-md > div > h2') # 使用新密码重新登录 Login.Login()._login('zhangsan',f'{random_number}') WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#index_nav_avatar')) ) self.driver.find_element(By.CSS_SELECTOR, '#index_nav_avatar').click() WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#index_user_settings')) ) self.driver.find_element(By.CSS_SELECTOR, '#index_user_settings').click() WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#settings_input_oldPassword')) ) oldpassword = self.driver.find_element(By.CSS_SELECTOR, '#settings_input_oldPassword') self.driver.execute_script("arguments[0].scrollIntoView();", oldpassword) oldpassword.clear() oldpassword.send_keys(random_number) newpassword = self.driver.find_element(By.CSS_SELECTOR, '#settings_input_newPassword') self.driver.execute_script("arguments[0].scrollIntoView();", newpassword) newpassword.clear() # 恢复密码为初始值 newpassword.send_keys(1234) cnewpassword = self.driver.find_element(By.CSS_SELECTOR, '#settings_input_passwordRepeat') self.driver.execute_script("arguments[0].scrollIntoView();", cnewpassword) cnewpassword.clear() cnewpassword.send_keys(1234) element = self.driver.find_element(By.CSS_SELECTOR, '#settings_submit_password') self.driver.execute_script("arguments[0].scrollIntoView();", element) WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#settings_submit_password')) ) time.sleep(0.5) element.click()
帖子编辑页自动化测试(Edit.py)
测试目标
-
确保帖子编辑页功能正常,包括输入标题、内容、板块选择及发布功能。
测试步骤
-
创建
Edit.py
,引入Utils.py
及相关依赖。 -
定义
Edit
类,管理帖子编辑页面的测试用例。 -
实现以下测试方法:
-
内容框测试:检查帖子内容输入框是否正确加载。
-
标题输入测试:输入标题并验证输入内容是否正确。
-
板块选择测试:选择不同板块,并检查选择结果是否正确。
-
发布测试:点击发布按钮并确保帖子成功发布并跳转到帖子列表页。
-
执行方式
-
在
RunTest.py
中调用Edit
类执行测试。import time from common.Utils import Driver from selenium.webdriver.common.by import By from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import Select class Edit: driver = '' url = '' def __init__(self): # 初始化方法,打开指定的URL self.url = 'http://127.0.0.1:9580/index.html' self.driver = Driver.driver self.driver.get(self.url) WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#bit-forum-content > div.page-header.d-print-none > div > div > div.col-auto.ms-auto.d-print-none > div > a.btn.btn-primary.d-none.d-sm-inline-block.article_post')) ) # 点击进入文章发布页面 self.driver.find_element(By.CSS_SELECTOR, '#bit-forum-content > div.page-header.d-print-none > div > div > div.col-auto.ms-auto.d-print-none > div > a.btn.btn-primary.d-none.d-sm-inline-block.article_post').click() def Content_Box(self): # 内容框测试,检查内容框是否存在 WebDriverWait(self.driver,10).until( EC.presence_of_element_located((By.CSS_SELECTOR,'#edit-article > div.CodeMirror.cm-s-default.CodeMirror-wrap.CodeMirror-empty > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre')) ) # 确认内容框是否已经加载 self.driver.find_element(By.CSS_SELECTOR,'#edit-article > div.CodeMirror.cm-s-default.CodeMirror-wrap.CodeMirror-empty > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre') def Title_Bar(self): # 标题栏测试,输入并验证标题内容 WebDriverWait(self.driver,10).until( EC.presence_of_element_located((By.CSS_SELECTOR,'#article_post_title')) ) self.driver.find_element(By.CSS_SELECTOR,'#article_post_title').send_keys(1) assert self.driver.find_element(By.CSS_SELECTOR,'#article_post_title').get_attribute('value') == "1" def Plate_Selection(self): # 板块选择测试,检查板块选择器中的选项 WebDriverWait(self.driver,10).until( EC.presence_of_element_located((By.CSS_SELECTOR,'#article_post_borad')) ) select = Select(self.driver.find_element(By.CSS_SELECTOR,'#article_post_borad')) options = [option.text for option in select.options] # 验证板块选项是否正确 assert 'C++' in options assert '前端技术' in options assert 'MySQL' in options assert '面试宝典' in options assert '经验分享' in options assert '招聘信息' in options assert '福利待遇' in options assert '灌水区' in options select.select_by_visible_text('C++') assert select.first_selected_option.text == 'C++' def Release(self): # 发布测试,点击发布按钮并验证发布反馈 WebDriverWait(self.driver,10).until( EC.element_to_be_clickable((By.CSS_SELECTOR,'#article_post_submit')) ) self.driver.execute_script("arguments[0].scrollIntoView();",self.driver.find_element(By.CSS_SELECTOR,'#article_post_submit')) time.sleep(0.5) self.driver.find_element(By.CSS_SELECTOR,'#article_post_submit').click() try: WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, 'body > div.jq-toast-wrap.bottom-right > div')) ) except: pass # 确认发布后的反馈消息出现 assert self.driver.find_element(By.CSS_SELECTOR,'body > div.jq-toast-wrap.bottom-right > div') def test_switch_section(self): """测试不同版块的切换功能""" try: # 等待“最新回复”板块加载,确保初始状态正确 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#bit-forum-content > div.page-body > div > div > div:nth-child(2) > h3').text.strip() == "最新回复" ) except: pass # 忽略异常,继续执行 # 断言当前版块为“最新回复” assert self.driver.find_element(By.CSS_SELECTOR, '#bit-forum-content > div.page-body > div > div > div:nth-child(2) > h3').text == "最新回复" # 切换到首页 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#nav_board_index > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#nav_board_index > a > span.nav-link-title').click() try: # 等待首页加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#bit-forum-content > div.page-header.d-print-none > div > div > div > h2').text == "首页" ) except: pass # 忽略异常 # 断言当前版块为“首页” assert self.driver.find_element(By.CSS_SELECTOR, '#bit-forum-content > div.page-header.d-print-none > div > div > div > h2').text == '首页' # 切换到“Java”版块 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#topBoardList > li:nth-child(2) > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#topBoardList > li:nth-child(2) > a > span.nav-link-title').click() try: # 等待“Java”版块加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == "Java" ) except: pass # 断言当前版块为“Java” assert self.driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == 'Java' # 切换到“C++”版块 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#topBoardList > li:nth-child(3) > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#topBoardList > li:nth-child(3) > a > span.nav-link-title').click() try: # 等待“C++”版块加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == "C++" ) except: pass # 断言当前版块为“C++” assert self.driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == 'C++' # 切换到“MySQL”版块 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#topBoardList > li:nth-child(5) > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#topBoardList > li:nth-child(5) > a > span.nav-link-title').click() try: # 等待“MySQL”版块加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == "MySQL" ) except: pass # 断言当前版块为“MySQL” assert self.driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == 'MySQL' def test_search_input(self): """测试搜索框输入功能""" WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.d-none.d-md-flex.me-3 > div > form > div > input')) ) self.driver.find_element(By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.d-none.d-md-flex.me-3 > div > form > div > input').send_keys( 123) # 断言输入的值是否正确 assert self.driver.find_element(By.CSS_SELECTOR, "body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.d-none.d-md-flex.me-3 > div > form > div > input").get_attribute( "value") == '123' def test_toggle_day_night_mode(self): """测试日间/夜间模式切换""" WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-dark > svg')) ) # 断言初始模式为日间模式 assert self.driver.find_element(By.CSS_SELECTOR, 'body').get_attribute("class") == 'theme-light' # 切换到夜间模式 self.driver.find_element(By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-dark > svg').click() WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-light > svg')) ) # 断言模式已切换为夜间模式 assert self.driver.find_element(By.CSS_SELECTOR, 'body').get_attribute("class") == 'theme-dark' # 切换回日间模式 self.driver.find_element(By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-light > svg').click() def test_message_button(self): """测试消息按钮是否可点击并弹出消息面板""" WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > div > a > svg')) ) self.driver.find_element(By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > div > a > svg').click() assert self.driver.find_element(By.CSS_SELECTOR, '#offcanvasEndLabel') def test_user_profile(self): """测试用户个人信息是否正确显示""" self.driver.fullscreen_window() WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#index_nav_nickname')) ) assert self.driver.find_element(By.CSS_SELECTOR, '#index_nav_nickname').text == 'zhangsan' WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#index_nav_name_sub')) ) assert self.driver.find_element(By.CSS_SELECTOR, '#index_nav_name_sub').text == '普通用户'
帖子详情页自动化测试(Detail.py)
测试目标
-
确保帖子详情页功能正常,包括查看、评论、点赞等功能。
测试步骤
-
创建
Detail.py
,引入Utils.py
及相关依赖。 -
定义
Detail
类,管理帖子详情页的测试用例。 -
实现以下测试方法:
-
查看博客测试:进入帖子详情页,确保内容正确显示。
-
评论测试:输入评论并提交,检查是否成功添加到页面。
-
点赞测试:点击点赞按钮,确认点赞数增加。
-
执行方式
-
在
RunTest.py
中调用Detail
类执行测试。import time from common.Utils import Driver from selenium.webdriver.common.by import By from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class Detail: """页面详情测试类,包含私信、帖子、评论、点赞等功能的测试""" driver = '' url = '' def __init__(self): """初始化 WebDriver 并访问指定 URL""" self.driver = Driver.driver self.url = 'http://127.0.0.1:9580/index.html' self.driver.get(self.url) # 等待页面关键元素加载 WebDriverWait(self.driver,10).until( EC.presence_of_element_located((By.CSS_SELECTOR,'#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong')) ) # 点击文章进入详情页 self.driver.find_element(By.CSS_SELECTOR,'#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong').click() def Private_Message(self): """测试私信发送功能""" WebDriverWait(self.driver,10).until( EC.presence_of_element_located((By.CSS_SELECTOR,'#btn_details_send_message')) ) self.driver.find_element(By.CSS_SELECTOR,'#btn_details_send_message').click() # 等待私信弹窗出现 WebDriverWait(self.driver,10).until( EC.visibility_of_element_located((By.CSS_SELECTOR,'#index_message_modal > div > div > div.modal-header > h5')) ) assert self.driver.find_element(By.CSS_SELECTOR,'#index_message_modal > div > div > div.modal-header > h5').text == '发送站内信' # 发送内容并提交 self.driver.find_element(By.CSS_SELECTOR,'#index_message_receive_content').send_keys('1') self.driver.find_element(By.CSS_SELECTOR,'#btn_index_send_message').click() # 等待发送成功的提示 WebDriverWait(self.driver,10).until( EC.visibility_of_element_located((By.CSS_SELECTOR,'body > div.jq-toast-wrap.bottom-right > div')) ) # assert '成功' in self.driver.find_element(By.CSS_SELECTOR,'body > div.jq-toast-wrap.bottom-right > div').text def Post(self): """测试帖子内容是否正确加载""" WebDriverWait(self.driver,10).until( EC.presence_of_element_located((By.CSS_SELECTOR,'#details_article_content > p')) ) WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#details_article_content_title')) ) def Comment(self): """测试评论功能""" WebDriverWait(self.driver,10).until( EC.visibility_of_element_located((By.CSS_SELECTOR,'#article_details_reply > div.CodeMirror.cm-s-default.CodeMirror-wrap.CodeMirror-empty > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre')) ) # 滚动到评论框 self.driver.execute_script("arguments[0].scrollIntoView();", self.driver.find_element(By.CSS_SELECTOR,'#article_details_reply > div.CodeMirror.cm-s-default.CodeMirror-wrap.CodeMirror-empty > div.CodeMirror-scroll > div.CodeMirror-sizer > div > div > div > div.CodeMirror-code > div > pre')) time.sleep(0.5) # 等待滚动完成 self.driver.find_element(By.CSS_SELECTOR,'#details_btn_article_reply > span').click() # 确认评论成功提示 WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, 'body > div.jq-toast-wrap.bottom-right > div')) ) assert '提示' in self.driver.find_element(By.CSS_SELECTOR, 'body > div.jq-toast-wrap.bottom-right > div').text def Likes(self): """测试点赞功能""" WebDriverWait(self.driver,10).until( EC.visibility_of_element_located((By.CSS_SELECTOR,'#details_article_likeCount')) ) old = int(self.driver.find_element(By.CSS_SELECTOR,'#details_article_likeCount').text) # 点击点赞按钮 self.driver.find_element(By.CSS_SELECTOR,'#details_btn_like_count').click() # 等待点赞成功提示 WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, 'body > div.jq-toast-wrap.bottom-right > div > h2')) ) # 验证点赞数是否增加 assert old + 1 == int(self.driver.find_element(By.CSS_SELECTOR,'#details_article_likeCount').text) def test_switch_section(self): """测试不同版块的切换功能""" try: # 等待“最新回复”板块加载,确保初始状态正确 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#bit-forum-content > div.page-body > div > div > div:nth-child(2) > h3').text.strip() == "最新回复" ) except: pass# 忽略异常,继续执行 # 断言当前版块为“最新回复” assert self.driver.find_element(By.CSS_SELECTOR,'#bit-forum-content > div.page-body > div > div > div:nth-child(2) > h3').text == "最新回复" # 切换到首页 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#nav_board_index > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#nav_board_index > a > span.nav-link-title').click() try: # 等待首页加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR,'#bit-forum-content > div.page-header.d-print-none > div > div > div > h2').text == "首页" ) except: pass# 忽略异常 # 断言当前版块为“首页” assert self.driver.find_element(By.CSS_SELECTOR,'#bit-forum-content > div.page-header.d-print-none > div > div > div > h2').text == '首页' # 切换到“Java”版块 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#topBoardList > li:nth-child(2) > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#topBoardList > li:nth-child(2) > a > span.nav-link-title').click() try: # 等待“Java”版块加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == "Java" ) except: pass # 断言当前版块为“Java” assert self.driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == 'Java' # 切换到“C++”版块 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#topBoardList > li:nth-child(3) > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#topBoardList > li:nth-child(3) > a > span.nav-link-title').click() try: # 等待“C++”版块加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == "C++" ) except: pass # 断言当前版块为“C++” assert self.driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == 'C++' # 切换到“MySQL”版块 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#topBoardList > li:nth-child(5) > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#topBoardList > li:nth-child(5) > a > span.nav-link-title').click() try: # 等待“MySQL”版块加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == "MySQL" ) except: pass # 断言当前版块为“MySQL” assert self.driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == 'MySQL' def test_search_input(self): """测试搜索框输入功能""" WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR,'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.d-none.d-md-flex.me-3 > div > form > div > input')) ) self.driver.find_element(By.CSS_SELECTOR,'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.d-none.d-md-flex.me-3 > div > form > div > input').send_keys(123) # 断言输入的值是否正确 assert self.driver.find_element(By.CSS_SELECTOR,"body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.d-none.d-md-flex.me-3 > div > form > div > input").get_attribute("value") == '123' def test_toggle_day_night_mode(self): """测试日间/夜间模式切换""" WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR,'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-dark > svg')) ) # 断言初始模式为日间模式 assert self.driver.find_element(By.CSS_SELECTOR, 'body').get_attribute("class") == 'theme-light' # 切换到夜间模式 self.driver.find_element(By.CSS_SELECTOR,'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-dark > svg').click() WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR,'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-light > svg')) ) # 断言模式已切换为夜间模式 assert self.driver.find_element(By.CSS_SELECTOR, 'body').get_attribute("class") == 'theme-dark' # 切换回日间模式 self.driver.find_element(By.CSS_SELECTOR,'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-light > svg').click() def test_message_button(self): """测试消息按钮是否可点击并弹出消息面板""" WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR,'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > div > a > svg')) ) self.driver.find_element(By.CSS_SELECTOR,'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > div > a > svg').click() assert self.driver.find_element(By.CSS_SELECTOR, '#offcanvasEndLabel') def test_user_profile(self): """测试用户个人信息是否正确显示""" self.driver.fullscreen_window() WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#index_nav_nickname')) ) assert self.driver.find_element(By.CSS_SELECTOR, '#index_nav_nickname').text == 'zhangsan' WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#index_nav_name_sub')) ) assert self.driver.find_element(By.CSS_SELECTOR, '#index_nav_name_sub').text == '普通用户'
首页自动化测试(HomePage.py)
测试目标
-
确保首页的基本交互功能正常,包括查看帖子列表、切换主题、搜索等。
测试步骤
-
创建
HomePage.py
,引入Utils.py
及相关依赖。 -
定义
HomePage
类,管理首页的测试用例。 -
实现以下测试方法:
-
博客列表测试:检查帖子列表是否正确加载。
-
搜索测试:输入关键词并验证搜索结果是否符合预期。
-
日夜模式切换测试:切换日间/夜间模式,并验证切换效果。
-
执行方式
-
在
RunTest.py
中调用HomePage
类执行测试。import time from common.Utils import Driver from selenium.webdriver.common.by import By from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class HomePage: driver = '' url = '' def __init__(self): # 初始化URL和driver self.url = 'http://127.0.0.1:9580/index.html' self.driver = Driver.driver self.driver.get(self.url) def Button(self): # 等待"发新贴"按钮可点击,并点击它 WebDriverWait(self.driver,10).until( EC.element_to_be_clickable((By.CSS_SELECTOR,'#bit-forum-content > div.page-header.d-print-none > div > div > div.col-auto.ms-auto.d-print-none > div > a.btn.btn-primary.d-none.d-sm-inline-block.article_post')) ) self.driver.find_element(By.CSS_SELECTOR,'#bit-forum-content > div.page-header.d-print-none > div > div > div.col-auto.ms-auto.d-print-none > div > a.btn.btn-primary.d-none.d-sm-inline-block.article_post').click() # 验证页面是否跳转到“发新贴” WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR,'#bit-forum-content > div.page-header.d-print-none > div > div > div > h2')) ) assert self.driver.find_element(By.CSS_SELECTOR,'#bit-forum-content > div.page-header.d-print-none > div > div > div > h2').text == '发新贴' def List(self): # 等待第一个帖子可点击,并点击它 WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong')) ) self.driver.find_element(By.CSS_SELECTOR,'#artical-items-body > div:nth-child(1) > div > div.col > div.text-truncate > a > strong').click() # 验证页面是否跳转到“最新回复”区域 WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR,'#bit-forum-content > div.page-body > div > div > div:nth-child(2) > h3')) ) assert self.driver.find_element(By.CSS_SELECTOR,'#bit-forum-content > div.page-body > div > div > div:nth-child(2) > h3').text == '最新回复' def test_switch_section(self): """测试不同版块的切换功能""" try: # 等待“最新回复”板块加载,确保初始状态正确 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#bit-forum-content > div.page-body > div > div > div:nth-child(2) > h3').text.strip() == "最新回复" ) except: pass # 忽略异常,继续执行 # 断言当前版块为“最新回复” assert self.driver.find_element(By.CSS_SELECTOR, '#bit-forum-content > div.page-body > div > div > div:nth-child(2) > h3').text == "最新回复" # 切换到首页 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#nav_board_index > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#nav_board_index > a > span.nav-link-title').click() try: # 等待首页加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#bit-forum-content > div.page-header.d-print-none > div > div > div > h2').text == "首页" ) except: pass # 忽略异常 # 断言当前版块为“首页” assert self.driver.find_element(By.CSS_SELECTOR, '#bit-forum-content > div.page-header.d-print-none > div > div > div > h2').text == '首页' # 切换到“Java”版块 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#topBoardList > li:nth-child(2) > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#topBoardList > li:nth-child(2) > a > span.nav-link-title').click() try: # 等待“Java”版块加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == "Java" ) except: pass # 断言当前版块为“Java” assert self.driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == 'Java' # 切换到“C++”版块 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#topBoardList > li:nth-child(3) > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#topBoardList > li:nth-child(3) > a > span.nav-link-title').click() try: # 等待“C++”版块加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == "C++" ) except: pass # 断言当前版块为“C++” assert self.driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == 'C++' # 切换到“MySQL”版块 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#topBoardList > li:nth-child(5) > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#topBoardList > li:nth-child(5) > a > span.nav-link-title').click() try: # 等待“MySQL”版块加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == "MySQL" ) except: pass # 断言当前版块为“MySQL” assert self.driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == 'MySQL' def test_search_input(self): """测试搜索框输入功能""" WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.d-none.d-md-flex.me-3 > div > form > div > input')) ) self.driver.find_element(By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.d-none.d-md-flex.me-3 > div > form > div > input').send_keys( 123) # 断言输入的值是否正确 assert self.driver.find_element(By.CSS_SELECTOR, "body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.d-none.d-md-flex.me-3 > div > form > div > input").get_attribute( "value") == '123' def test_toggle_day_night_mode(self): """测试日间/夜间模式切换""" WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-dark > svg')) ) # 断言初始模式为日间模式 assert self.driver.find_element(By.CSS_SELECTOR, 'body').get_attribute("class") == 'theme-light' # 切换到夜间模式 self.driver.find_element(By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-dark > svg').click() WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-light > svg')) ) # 断言模式已切换为夜间模式 assert self.driver.find_element(By.CSS_SELECTOR, 'body').get_attribute("class") == 'theme-dark' # 切换回日间模式 self.driver.find_element(By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-light > svg').click() def test_message_button(self): """测试消息按钮是否可点击并弹出消息面板""" WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > div > a > svg')) ) self.driver.find_element(By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > div > a > svg').click() assert self.driver.find_element(By.CSS_SELECTOR, '#offcanvasEndLabel') def test_user_profile(self): """测试用户个人信息是否正确显示""" self.driver.fullscreen_window() WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#index_nav_nickname')) ) assert self.driver.find_element(By.CSS_SELECTOR, '#index_nav_nickname').text == 'zhangsan' WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#index_nav_name_sub')) ) assert self.driver.find_element(By.CSS_SELECTOR, '#index_nav_name_sub').text == '普通用户'
私信功能自动化测试(PrivateMessage.py)
测试目标
-
确保私信功能正常,包括查看和发送私信。
测试步骤
-
创建
PrivateMessage.py
,引入Utils.py
及相关依赖。 -
定义
PrivateMessage
类,管理私信的测试用例。 -
实现以下测试方法:
-
查看私信测试:进入私信列表并点击某条私信,检查内容是否正确显示。
-
发送私信测试:输入内容并发送,确保私信成功发送。
-
执行方式
-
在
RunTest.py
中调用PrivateMessage
类执行测试。import time from common.Utils import Driver from selenium.webdriver.common.by import By from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class PrivateMessage: url = '' driver = '' def __init__(self): self.driver = Driver.driver self.url = 'http://127.0.0.1:9580/index.html' self.driver.get(self.url) WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR,'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > div > a > svg')) ) self.driver.find_element(By.CSS_SELECTOR,'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > div > a > svg').click() assert self.driver.find_element(By.CSS_SELECTOR, '#offcanvasEndLabel') def reply(self): # 等待并点击私信列表中的第一条消息 WebDriverWait(self.driver,10).until( EC.presence_of_element_located((By.CSS_SELECTOR,'#index_div_message_list > div:nth-child(1) > div > div.col.text-truncate > a')) ) self.driver.find_element(By.CSS_SELECTOR,'#index_div_message_list > div:nth-child(1) > div > div.col.text-truncate > a').click() # 等待 "取消回复" 按钮出现并点击 WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, '#index_message_reply_cancel')) ) self.driver.find_element(By.CSS_SELECTOR,'#index_message_reply_cancel').click() # 再次点击同一条私信,重新进入回复界面 WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#index_div_message_list > div:nth-child(1) > div > div.col.text-truncate > a')) ) self.driver.find_element(By.CSS_SELECTOR,'#index_div_message_list > div:nth-child(1) > div > div.col.text-truncate > a').click() # 等待 "回复" 按钮出现并点击 WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, '#btn_index_message_reply')) ) self.driver.find_element(By.CSS_SELECTOR,'#btn_index_message_reply').click() # 等待回复输入框并输入消息 WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#index_message_reply_receive_content')) ) self.driver.find_element(By.CSS_SELECTOR,'#index_message_reply_receive_content').send_keys(123) # 等待 "发送回复" 按钮出现并点击 WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, '#btn_index_send_message_reply')) ) self.driver.find_element(By.CSS_SELECTOR, '#btn_index_send_message_reply').click() # 等待并检查是否有成功提示 WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, 'body > div.jq-toast-wrap.bottom-right > div')) ) assert '成功' in self.driver.find_element(By.CSS_SELECTOR, 'body > div.jq-toast-wrap.bottom-right > div').text
我的帖子自动化测试(MyPost.py)
测试目标
-
确保“我的帖子”页面功能正常,包括查看、管理已发布的帖子。
测试步骤
-
创建
MyPost.py
,引入Utils.py
及相关依赖。 -
定义
MyPost
类,管理“我的帖子”页面的测试用例。 -
实现以下测试方法:
-
查看已发布帖子:进入“我的帖子”页面,检查列表是否正确显示。
-
编辑帖子测试:点击某个帖子进行编辑,检查是否能正常修改并保存。
-
删除帖子测试:删除某个帖子,并检查列表是否正确更新。
-
执行方式
-
在
RunTest.py
中调用MyPost
类执行测试。import time from common.Utils import Driver from selenium.webdriver.common.by import By from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class MyPost: driver = '' url = '' def __init__(self): # 初始化URL和driver,打开用户首页 self.url = 'http://127.0.0.1:9580/index.html' self.driver = Driver.driver self.driver.get(self.url) # 等待页面加载完毕,点击头像进入用户资料页面 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#index_nav_avatar')) ) self.driver.find_element(By.CSS_SELECTOR, '#index_nav_avatar').click() # 等待用户资料链接可点击,进入用户资料页面 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR,'#index_user_profile')) ) self.driver.find_element(By.CSS_SELECTOR,'#index_user_profile').click() def Name(self): # 等待用户昵称加载完成,并验证昵称是否为 'zhangsan' WebDriverWait(self.driver,10).until( EC.presence_of_element_located((By.CSS_SELECTOR,'#profile_nickname')) ) # 验证昵称是否正确 assert self.driver.find_element(By.CSS_SELECTOR,'#profile_nickname').text == 'zhangsan' def Post(self): # 等待第一个帖子可点击 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#profile_article_body > li:nth-child(1) > div.card.timeline-event-card > div > div > div > div > div.text-truncate > a > strong')) ) self.driver.find_element(By.CSS_SELECTOR,'#profile_article_body > li:nth-child(1) > div.card.timeline-event-card > div > div > div > div > div.text-truncate > a > strong').click() try: # 等待文章编辑按钮出现,若出现则表示该帖子可以编辑 WebDriverWait(self.driver,10).until( EC.presence_of_element_located((By.CSS_SELECTOR,'#details_artile_edit')) ) except: pass # 验证是否可以编辑帖子 assert self.driver.find_element(By.CSS_SELECTOR,'#details_artile_edit') def test_switch_section(self): """测试不同版块的切换功能""" try: # 等待“最新回复”板块加载,确保初始状态正确 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#bit-forum-content > div.page-body > div > div > div:nth-child(2) > h3').text.strip() == "最新回复" ) except: pass # 忽略异常,继续执行 # 断言当前版块为“最新回复” assert self.driver.find_element(By.CSS_SELECTOR, '#bit-forum-content > div.page-body > div > div > div:nth-child(2) > h3').text == "最新回复" # 切换到首页 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#nav_board_index > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#nav_board_index > a > span.nav-link-title').click() try: # 等待首页加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#bit-forum-content > div.page-header.d-print-none > div > div > div > h2').text == "首页" ) except: pass # 忽略异常 # 断言当前版块为“首页” assert self.driver.find_element(By.CSS_SELECTOR, '#bit-forum-content > div.page-header.d-print-none > div > div > div > h2').text == '首页' # 切换到“Java”版块 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#topBoardList > li:nth-child(2) > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#topBoardList > li:nth-child(2) > a > span.nav-link-title').click() try: # 等待“Java”版块加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == "Java" ) except: pass # 断言当前版块为“Java” assert self.driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == 'Java' # 切换到“C++”版块 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#topBoardList > li:nth-child(3) > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#topBoardList > li:nth-child(3) > a > span.nav-link-title').click() try: # 等待“C++”版块加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == "C++" ) except: pass # 断言当前版块为“C++” assert self.driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == 'C++' # 切换到“MySQL”版块 WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#topBoardList > li:nth-child(5) > a > span.nav-link-title')) ) self.driver.find_element(By.CSS_SELECTOR, '#topBoardList > li:nth-child(5) > a > span.nav-link-title').click() try: # 等待“MySQL”版块加载 WebDriverWait(self.driver, 10).until( lambda driver: driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == "MySQL" ) except: pass # 断言当前版块为“MySQL” assert self.driver.find_element(By.CSS_SELECTOR, '#article_list_board_title').text == 'MySQL' def test_search_input(self): """测试搜索框输入功能""" WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.d-none.d-md-flex.me-3 > div > form > div > input')) ) self.driver.find_element(By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.d-none.d-md-flex.me-3 > div > form > div > input').send_keys( 123) # 断言输入的值是否正确 assert self.driver.find_element(By.CSS_SELECTOR, "body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div.nav-item.d-none.d-md-flex.me-3 > div > form > div > input").get_attribute( "value") == '123' def test_toggle_day_night_mode(self): """测试日间/夜间模式切换""" WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-dark > svg')) ) # 断言初始模式为日间模式 assert self.driver.find_element(By.CSS_SELECTOR, 'body').get_attribute("class") == 'theme-light' # 切换到夜间模式 self.driver.find_element(By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-dark > svg').click() WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located((By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-light > svg')) ) # 断言模式已切换为夜间模式 assert self.driver.find_element(By.CSS_SELECTOR, 'body').get_attribute("class") == 'theme-dark' # 切换回日间模式 self.driver.find_element(By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > a.nav-link.px-0.hide-theme-light > svg').click() def test_message_button(self): """测试消息按钮是否可点击并弹出消息面板""" WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > div > a > svg')) ) self.driver.find_element(By.CSS_SELECTOR, 'body > div.page > header.navbar.navbar-expand-md.navbar-light.d-print-none > div > div > div:nth-child(2) > div > a > svg').click() assert self.driver.find_element(By.CSS_SELECTOR, '#offcanvasEndLabel') def test_user_profile(self): """测试用户个人信息是否正确显示""" self.driver.fullscreen_window() WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#index_nav_nickname')) ) assert self.driver.find_element(By.CSS_SELECTOR, '#index_nav_nickname').text == 'zhangsan' WebDriverWait(self.driver, 10).until( EC.element_to_be_clickable((By.CSS_SELECTOR, '#index_nav_name_sub')) ) assert self.driver.find_element(By.CSS_SELECTOR, '#index_nav_name_sub').text == '普通用户'
运行所有测试(RunTest.py)
测试目标
-
依次执行所有测试,并确保最终关闭 WebDriver。
测试步骤
-
创建
RunTest.py
,引入Utils.py
及所有测试文件。 -
依次调用各个测试类的测试方法。
-
统一管理 WebDriver 的初始化和退出。
执行方式
-
直接运行
RunTest.py
,所有测试会自动执行。
import time
from common.Utils import Driver
from cases import Login,PersonalHomepage,Edit,Detail,PrivateMessage,HomePage,MyPost,Register
# 运行登录相关的测试
def run_Login_tests():
"""执行所有登录相关的测试"""
login = Login.Login() # 只实例化一次,避免重复创建对象
login.login_suc_test1()
login.login_suc_test2()
login.test_login_empty_username_empty_password()
login.test_login_empty_username_valid_password()
login.test_login_empty_username_invalid_password()
login.test_login_invalid_username_empty_password()
login.test_login_valid_username_empty_password()
login.test_login_valid_username_invalid_password()
login.test_login_invalid_username_valid_password()
login.test_login_invalid_username_invalid_password()
# 运行个人主页相关的测试
def run_PersonalHomepage_tests():
"""执行所有个人主页相关的测试"""
personal_home = PersonalHomepage.PersonalHomepage()
personal_home.test_switch_section()
personal_home.test_search_input()
personal_home.test_toggle_day_night_mode()
personal_home.test_message_button()
personal_home.test_user_profile()
personal_home.test_change_avatar()
personal_home.test_nickname_field()
personal_home.test_email_field()
personal_home.test_phone_number_field()
personal_home.test_bio_field()
personal_home.test_change_password()
# 运行帖子编辑相关的测试
def run_Edit_tests():
"""执行所有帖子编辑相关的测试"""
edit = Edit.Edit()
edit.Content_Box()
edit.Title_Bar()
edit.Plate_Selection()
edit.Release()
edit.test_switch_section()
edit.test_search_input()
edit.test_toggle_day_night_mode()
edit.test_message_button()
edit.test_user_profile()
# 运行帖子详情相关的测试
def run_Detail_tests():
"""执行所有帖子详情页面相关的测试"""
detail = Detail.Detail()
detail.Private_Message()
detail.Post()
detail.Comment()
detail.Likes()
detail.test_switch_section()
detail.test_search_input()
detail.test_toggle_day_night_mode()
detail.test_message_button()
detail.test_user_profile()
# 运行私信功能的测试
def run_PrivateMessage_tests():
"""执行私信功能的测试"""
private_msg = PrivateMessage.PrivateMessage()
private_msg.reply()
# 运行首页相关的测试
def run_HomePage_tests():
"""执行所有首页相关的测试"""
home = HomePage.HomePage()
home.Button()
home.List()
home.test_switch_section()
home.test_search_input()
home.test_toggle_day_night_mode()
home.test_message_button()
home.test_user_profile()
# 运行“我的帖子”相关的测试
def run_MyPost_test():
"""执行所有‘我的帖子’相关的测试"""
my_post = MyPost.MyPost()
my_post.Name()
my_post.Post()
my_post.test_switch_section()
my_post.test_search_input()
my_post.test_toggle_day_night_mode()
my_post.test_message_button()
my_post.test_user_profile()
# 运行注册功能的测试
def run_Register_test():
"""执行所有注册功能的测试"""
register = Register.Register()
register.test_valid_inputs_agree_terms()
register.test_valid_username_empty_others_disagree_terms()
register.test_empty_username_valid_nickname_mixed_passwords_disagree_terms()
register.test_empty_username_nickname_valid_password_agree_terms()
register.test_valid_inputs_missing_confirm_password_disagree_terms()
register.test_valid_username_empty_nickname_password_confirmed_agree_terms()
register.test_invalid_inputs_disagree_terms()
# 运行所有测试
def run_all_tests():
"""依次执行所有的测试,并在结束后关闭 WebDriver"""
try:
run_Login_tests()
run_PersonalHomepage_tests()
run_Edit_tests()
run_Detail_tests()
run_PrivateMessage_tests()
run_HomePage_tests()
run_MyPost_test()
run_Register_test()
except Exception as e:
print(f"错误发生: {e}") # 捕获异常,避免测试中断
finally:
Driver.driver.quit() # 测试完成后关闭 WebDriver
# 主函数
if __name__ == '__main__':
run_all_tests()
可优化部分
- 菜单反复调用测试,可以专门写一个对菜单功能测试的函数
- 注释没有统一格式
- 好多地方截图没补上去
前面几个像登录测试注册那种选择多的是用 allpairs搞定的
3.各函数作用总览
Utils.py
-
__init__(self)
: 初始化 WebDriver。 -
getScreenShot(self)
: 截取当前页面截图并保存。
Login.py
-
__init__(self)
: 初始化 WebDriver 并打开登录页面。 -
_login(self, username, password)
: 输入用户名和密码,执行登录操作。 -
_verify_login_success(self)
: 验证用户是否成功登录。 -
login_suc_test1(self)
: 测试正确的用户名和密码能否成功登录。 -
login_suc_test2(self)
: 测试另一个正确的用户名和密码登录是否成功。 -
test_login_empty_username_empty_password(self)
: 测试用户名和密码都为空的情况。 -
test_login_valid_username_empty_password(self)
: 测试用户名正确但密码为空的情况。 -
test_login_invalid_username_valid_password(self)
: 测试用户名错误但密码正确的情况。
Register.py
-
__init__(self)
: 初始化 WebDriver 并打开注册页面。 -
test_valid_inputs_agree_terms(self)
: 测试所有输入正确且同意条款的情况。 -
test_valid_username_empty_others_disagree_terms(self)
: 测试仅填写用户名,其他字段为空且不同意条款的情况。 -
test_empty_username_valid_nickname_mixed_passwords_disagree_terms(self)
: 测试用户名为空、昵称有效但密码不匹配的情况。 -
test_empty_username_nickname_valid_password_agree_terms(self)
: 测试用户名和昵称为空,密码正确且同意条款的情况。
Edit.py
-
__init__(self)
: 初始化 WebDriver 并进入编辑页面。 -
Content_Box(self)
: 检查博客内容输入框是否正确加载。 -
Title_Bar(self)
: 输入标题并验证输入是否正确。 -
Plate_Selection(self)
: 选择不同的板块,并检查选择结果是否正确。 -
Release(self)
: 点击发布按钮,确保博客成功发布。 -
test_switch_section(self)
: 测试博客发布后页面是否能正确跳转到博客列表。
Detail.py
-
__init__(self)
: 初始化 WebDriver 并打开博客详情页。 -
Private_Message(self)
: 测试私信功能,确保发送私信成功。 -
Post(self)
: 测试博客内容是否正确显示。 -
Comment(self)
: 测试用户能否成功发表评论。 -
Likes(self)
: 测试点赞按钮是否正常工作。
PersonalHomepage.py
-
__init__(self)
: 初始化 WebDriver 并打开个人主页。 -
test_change_avatar(self)
: 测试头像修改功能。 -
test_nickname_field(self)
: 测试修改昵称并检查是否保存成功。 -
test_email_field(self)
: 测试修改邮箱并验证更新后的信息。 -
test_phone_number_field(self)
: 测试修改手机号功能。 -
test_bio_field(self)
: 测试个人简介的编辑与保存功能。 -
test_change_password(self)
: 测试修改密码,并使用新密码重新登录。
PrivateMessage.py
-
__init__(self)
: 初始化 WebDriver 并进入私信页面。 -
reply(self)
: 测试私信回复功能,确保发送成功。
HomePage.py
-
__init__(self)
: 初始化 WebDriver 并打开首页。 -
Button(self)
: 测试首页“发新贴”按钮是否能正常跳转到编辑页面。 -
List(self)
: 测试博客列表是否正确加载,并可点击查看详情。 -
test_search_input(self)
: 测试搜索框输入内容,并检查搜索结果。 -
test_toggle_day_night_mode(self)
: 测试日间/夜间模式的切换功能。
MyPost.py
-
__init__(self)
: 初始化 WebDriver 并进入“我的帖子”页面。 -
Name(self)
: 检查用户名是否正确显示。 -
Post(self)
: 测试点击某个帖子,确保页面跳转正常。
Test.py
-
run_all_tests()
: 运行所有测试用例,确保所有功能正常执行。
4.测试环境与执行方式
运行环境
-
Python 版本:3.13
-
依赖库:
selenium
-
浏览器:Microsoft Edge 版本 134.0.3124.68 (正式版本) (64 位)
-
WebDriver:
msedgedriver
需正确配置
运行方式
-
安装
selenium
依赖:pip install selenium
-
确保
msedgedriver
已正确安装。 -
运行
RunCases.py
以执行完整的测试流程:python RunCases.py
结果验证
-
所有测试案例执行完成后,测试日志将会记录各个测试步骤的执行结果。
-
关键页面截图将在
images/
目录中保存,以供参考。 -
若有测试失败,请参考日志进行问题排查,并根据错误信息调整代码或系统设置。
5.自动化框架特点与亮点
特点
该自动化测试框架结合了 Selenium WebDriver 与 自定义测试模块,能够高效地验证论坛的各项功能。测试用例覆盖了从 用户注册、登录、发布帖子、浏览帖子、私信交互、个人信息管理 到 页面跳转 等多个关键场景。
所有测试用例都封装为独立的函数,目标明确,执行步骤清晰,便于维护和扩展。自动化测试可以反复执行,提高测试覆盖率的同时,也减少了人工干预,提高测试效率。
此外,该框架还利用 Selenium WebDriver 的等待机制,确保页面元素完全加载后再执行操作,减少因页面加载缓慢导致的测试失败。同时,截图功能 使得测试过程可追踪,异常情况可视化,便于快速定位问题。
亮点
1. 统一的 WebDriver 管理
-
采用 Driver 类 统一管理 WebDriver 实例,避免每个测试用例重复创建 WebDriver,减少资源浪费,提高执行效率。
-
通过 单例模式 复用 WebDriver,在多个测试用例间共享浏览器会话,减少重复初始化的开销。
2. 功能清晰的测试用例
-
每个测试用例都封装成独立的函数,清晰定义测试目标和验证条件,增强了代码的可读性和可维护性。
-
测试覆盖了帖子网站的核心功能,包括:
-
用户注册与登录:模拟不同用户的登录情况,验证账号验证逻辑。
-
帖子管理:测试帖子的发布、编辑、删除等功能,确保用户可以正常管理文章。
-
交互功能:测试评论、私信、点赞等用户互动功能,确保社交体验顺畅。
-
3. 高效的等待机制
-
结合 显式等待 (WebDriverWait) 和 隐式等待,有效解决页面加载速度和测试执行速度不一致的问题。
-
避免因为页面元素未完全渲染而导致的
NoSuchElementException
或ElementNotInteractableException
,提高测试稳定性和可靠性。
4. 自动截图功能
-
每个测试用例执行时,自动截图关键操作,便于在测试失败时进行问题追踪和回溯。
-
结合 日志记录功能,在测试失败时保存错误信息,帮助开发人员快速定位和修复问题。
5. 自动化的重复执行
-
该框架可以轻松地多次执行同一组测试,适用于回归测试和稳定性测试,确保系统在不同时间段或高并发情况下依然表现正常。
-
结合 定时任务 (cron jobs) 或 CI/CD 流水线,可实现每日自动化回归测试,保证系统功能始终可用。
6. 页面元素检查与交互验证
-
通过 WebDriver 的
presence_of_element_located()
和visibility_of_element_located()
方法,确保所有关键元素在交互前正确加载。 -
检测页面上的按钮、表单、文本框等控件是否可点击、可输入、可提交,防止界面交互异常。
6.性能测试
使用 Apache JMeter 对 论坛系统 进行性能测试,模拟不同用户操作下的系统响应情况,确保系统能够在高并发访问下稳定运行。
测试目标
-
评估 用户登录、浏览帖子、查看详情、发表帖子 等关键功能的响应时间和吞吐量
-
通过 并发请求模拟大量用户访问,检查系统的稳定性
-
记录并分析 响应时间、线程活动情况、事务处理速度,优化系统性能
测试步骤
-
HTTP 请求默认值设置:定义论坛系统的基本 URL,简化后续请求配置
-
HTTP 头信息管理器:设置请求头,模拟真实浏览器访问
-
自定义变量:定义通用变量,便于动态参数传递
-
账号密码 CSV 数据管理:使用 CSV 文件导入用户信息,模拟多个用户登录
-
HTTP Cookie 管理器:存储会话信息,确保用户身份在请求间保持一致
-
执行测试事务:
-
用户登录:模拟用户输入账号密码并登录
-
用户信息:获取用户的个人资料
-
列表页:访问帖子列表,测试加载速度
-
详情页:查看特定帖子内容,测试响应时间
-
添加帖子:模拟用户发布新帖,验证提交效率
-
-
结果分析
-
查看结果树:检查每个请求的详细响应数据
-
聚合报告:统计请求数量、平均响应时间、吞吐量等指标
-
JMeter 监控插件
-
Active Threads Over Time:观察系统在不同时间点的并发用户数
-
Response Times Over Time:分析不同时间点的服务器响应时间
-
Transactions per Second:统计系统每秒钟处理的事务数
-
-
列表那异常爆表了啊
应该是那一页数据太多的问题
性能测试还在学,这就测的大概,看个乐
8.完整测试代码连接
GitHub - bbppooii/Automated-testing-of-forum-system
9.补充
补了个错误总结