关于MindVault项目测试报告
目录
1.项目背景
2.编写目的
3.测试环境
4.测试内容
4.1.编写测试用例
【1】登录测试
【2】文章列表页及文章数量测试
【3】文章详情页测试
【4】文章编辑与发布测试
【5】删除文章测试
【6】退出当前账号测试
4.2.使用Selenium进行Web自动化测试(Python)
4.2.1 安装 WebDriverManager 和 Selenium
4.2.2 创建测试项目
4.2.3 编写自动化测试脚本
Utils.py
RunCase.py
BlogLogin.py
BlogList.py
BlogEdit.py
BlogDetail.py
BlogCancellation.py
5.测试内容外的BUG分析
1.项目背景
本项目是基于SSM框架(Spring、Spring MVC、MyBatis)开发的共享文章阅读项目,包括用户登录、文章发布、编辑、删除、查看等功能,提供文章列表页和文章详情页展示文章内容。用户登录后可以查看自己和其他用户的文章,并通过系统记录文章的发布时间、标题、发布者信息。项目采用Selenium自动化测试进行功能验证,测试内容涵盖用户登录、文章列表展示、用户信息校验、文章数量统计、查看详情、文章发布与编辑、删除文章等,确保系统的稳定性和功能完整性。
2.编写目的
本报告为MindVault系统的安全测试报告,目的在考察软件安全性,测试结论以及测试建议。
3.测试环境
操作系统:Windows 11 |
浏览器及版本:Microsoft Edge版本 135.0.3179.98 (正式版本) (64 位) |
网络:WLAN局域网 |
设备:PC电脑一台,运行内存:16G,存储空间:1TG |
分辨率:2560*1600 |
4.测试内容
4.1.编写测试用例
【1】登录测试
介绍:系统已经在数据库中存储了用户名和密码,用户成功登录后会跳转至文章列表页;执行注销操作后,将返回登录页
测试用例:
(a)界面展示
(b)输入正确的账号和密码(如:账号 zhangsan ,密码 123456)
预期结果:成功登录,并跳转至文章列表页
实际结果:
(c)输入错误的账号或密码(如: 账号 zhangsan ,密码 123)
预期结果:提示“账号或密码错误”
实际结果:
【2】文章列表页及文章数量测试
介绍:登录成功后,系统将跳转至文章列表页面,该页面显示文字的总数量、当前用户的头像和用户名,并提供文章的基本信息(如标题、发布时间及部分内容)
测试用例:
(a)列表页展示(文章数量大于0)
【3】文章详情页测试
介绍:用户点击任意一篇文章的“查看全文”按钮,即可进入文章详情页面,查看完整内容。
-若文章属于当前登录用户,则页面提供“编辑”和“删除”选项
-若文章属于其他用户,则仅支持阅读,不提供编辑和删除权限
测试用例:
(a)详情页界面
(a-1)其他用户的文章详情页
预期结果:页面显示文章标题和内容,不包含“编辑”和“删除”按钮。
实际结果:
(a-2)当前用户的文章详情页
预期结果:页面显示文章标题和内容,并提供“编辑”和“删除”按钮
实际结果:
【4】文章编辑与发布测试
介绍:用户可通过文章列表页右上角的“写文章”按钮进入文章编辑页面,并进行新文章的编写和发布。
测试用例:
(a)登录后跳转至文章列表页
(b)点击“写文章”按钮,跳转至文章编辑页面
(c)输入标题和内容
预期结果:标题和内容不能为空,且输入的内容符合规范。
实际结果:
(d)点击“发布文章”按钮
预期结果:文章成功发布,并跳转回文章列表页;新发布的文章位于列表顶部,并显示文章标题、发布时间及部分内容(若博客内容较长)。
【5】删除文章测试
介绍:在文章列表页面,点击当前用户发布的文章的“查看全文”按钮,进入详情页面后点击“删除”按钮,即可删除该文章,并返回文章列表页。删除后,该文章将不再显示。
测试用例:
(a)文章列表页展示
(b)点击“查看全文”按钮,进入文章详情页
(c)点击”删除“按钮
预期结果:弹窗提醒,确定后文章成功删除,返回文章列表页,且该文章不再显示,文章数量减少1。
实际结果:
【6】退出当前账号测试
介绍:用户在文章列表页面点击“注销”按钮后,将返回登录页面,并清空账号和密码输入框内容。
测试用例:
(a)文章列表页展示
(b)点击右上角“注销”按钮
预期结果:成功退出,跳转至登录页面,且账号和密码输入框为空。
实际结果:
4.2.使用Selenium进行Web自动化测试(Python)
4.2.1 安装 WebDriverManager 和 Selenium
WebDriverManager 是用于管理 Web 驱动程序的工具,在进行 Selenium 测试时,浏览器需要对应的 WebDriver 进行控制和操作。WebDriverManager 能够自动检测本地浏览器版本并下载匹配的驱动程序(如 ChromeDriver、FirefoxDriver 等),避免手动下载和配置的繁琐过程,提高测试效率和稳定性。
4.2.2 创建测试项目
1.在项目中打开 File → Settings,进入设置界面。
2.在“Project: 当前项目名”下,检查是否已安装 selenium
和 webdriver-manager
依赖。
3.若未安装,可使用以下命令进行安装:
pip install selenium webdriver-manager
4.2.3 编写自动化测试脚本
脚本结构:
Utils.py
import datetime
import os
import sys
from webdriver_manager.microsoft import EdgeChromiumDriverManager
from selenium import webdriver
from selenium.webdriver.edge.service import Serviceclass Driver:driver = Nonedef __init__(self):"""初始化WebDriver并启动浏览器"""options = webdriver.EdgeOptions()options.page_load_strategy = 'eager'#options.add_argument('--disable-popup-blocking') # 禁用弹窗拦截#options.add_argument('--disable-notifications') # 禁用通知弹窗# 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()
RunCase.py
import time
from common.Utils import BlogDriver
from cases import BlogLogin, BlogList, BlogEdit, BlogDetail, BlogCancellationdef run_blog_login_tests():"""运行登录模块的所有测试"""print("Running BlogLogin tests...")BlogLogin.BlogLogin().loginSucTest1() # 测试用户名 zhangsan 的登录BlogLogin.BlogLogin().loginSucTest2() # 测试用户名 lisi 的登录BlogLogin.BlogLogin().test_login_empty_username_empty_password() # 测试用户名和密码都为空BlogLogin.BlogLogin().test_login_empty_username_valid_password() # 测试用户名为空,密码有效BlogLogin.BlogLogin().test_login_invalid_username_invalid_password() # 测试用户名和密码无效def run_blog_list_tests():"""运行博客列表页的所有测试"""print("Running BlogList tests...")BlogList.BlogList().test_account_name_display() # 测试用户名是否正确显示BlogList.BlogList().test_article_count_display() # 测试文章数是否正确显示BlogList.BlogList().test_blog_title_exists() # 测试博客标题是否存在BlogList.BlogList().test_blog_date_exists() # 测试博客发布日期是否存在BlogList.BlogList().test_blog_content_exists() # 测试博客内容简介是否存在BlogList.BlogList().test_blog_button_exists() # 测试博客详情按钮是否存在BlogList.BlogList().test_navigation_to_blog_editor() # 测试博客编辑页跳转BlogList.BlogList().test_logout_redirect_to_login_page() # 测试登出后是否跳转到登录页def run_blog_edit_tests():"""运行博客编辑页的所有测试"""print("Running BlogEdit tests...")BlogEdit.BlogEdit().test_publish_redirect_list() # 测试发布后是否跳转到博客列表页def run_blog_detail_tests():"""运行博客详情页的所有测试"""print("Running BlogDetail tests...")BlogDetail.BlogDetail().test_current_user_can_edit() # 测试当前用户是否能编辑博客time.sleep(3)BlogDetail.BlogDetail().test_current_user_can_delete() # 测试当前用户是否能删除博客time.sleep(3)BlogDetail.BlogDetail().test_other_users_cannot_edit_delete() # 测试其他用户是否能编辑或删除博客time.sleep(3)def run_blog_cancellation_tests():"""运行博客注销的所有测试"""print("Running BlogCancellation tests...")BlogCancellation.BlogCancellation().test_logout_success() # 测试注销是否成功并限制访问def run_all_tests():"""运行所有测试"""run_blog_login_tests() # 运行登录相关测试run_blog_list_tests() # 运行博客列表相关测试BlogLogin.BlogLogin().loginSucTest1() # 重新登录run_blog_edit_tests() # 运行博客编辑相关测试run_blog_detail_tests() # 运行博客详情页相关测试run_blog_cancellation_tests() # 运行博客注销相关测试print("所有测试已成功通过!")if __name__ == "__main__":try:run_all_tests()except Exception as e:print(f"错误发生: {e}")finally:BlogDriver.driver.quit() # 结束所有测试后关闭浏览器
BlogLogin.py
import time
from common.Utils import BlogDriver
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.common.exceptions import NoAlertPresentException, TimeoutException
from selenium.webdriver.common.alert import Alertclass BlogLogin:url = ''driver = ''def __init__(self):"""初始化登录页面并打开指定URL"""self.url = "http://117.72.87.234:8080/blog_login.html"self.driver = BlogDriver.driverself.driver.get(self.url)def loginSucTest1(self):"""测试用户 'zhangsan' 的登录成功"""self._login('zhangsan', '123456')self._verify_login_success()self.driver.back()def loginSucTest2(self):"""测试用户 'lisi' 的登录成功"""self._login('lisi', '123456')self._verify_login_success()self.driver.back()def _login(self, username, password):"""封装的登录方法,接收用户名和密码"""WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, '#username')))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()# 增加弹窗处理try:alert = WebDriverWait(self.driver, 3).until(EC.alert_is_present())alert_text = alert.textalert.accept() # 关闭弹窗return alert_text # 返回弹窗内容(可选)except:pass # 如果没有弹窗,继续执行def _verify_login_success(self):"""验证用户是否成功登录"""BlogDriver.getScreenShot()try:WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, 'body > div.container > div.left > div > h3')))return Trueexcept TimeoutException:return Falsedef test_login_empty_username_empty_password(self):"""测试用户名和密码为空时的登录行为"""self._login('', '')self._verify_alert('登录失败,用户名或密码不能为空')self.driver.back()def test_login_empty_username_valid_password(self):"""测试用户名为空,密码有效时的登录行为"""self._login('', '123456')self._verify_alert('登录失败,用户名或密码不能为空')self.driver.back()def test_login_empty_username_invalid_password(self):"""测试用户名为空,密码无效时的登录行为"""self._login('', '123')self._verify_alert('登录失败,用户名或密码不能为空')self.driver.back()def test_login_invalid_username_empty_password(self):"""测试用户名无效,密码为空时的登录行为"""self._login('123', '')self._verify_alert('登录失败,用户名或密码不能为空')self.driver.back()def test_login_valid_username_empty_password(self):"""测试用户名有效,密码为空时的登录行为"""self._login('123', '')self._verify_alert('登录失败,用户名或密码不能为空')self.driver.back()def test_login_valid_username_invalid_password(self):"""测试用户名有效,密码无效时的登录行为"""self._login('lisi', '123')self._verify_alert('密码错误')self.driver.back()def test_login_invalid_username_valid_password(self):"""测试用户名无效,密码有效时的登录行为"""self._login('123', '123456')self._verify_alert('用户不存在')self.driver.back()def test_login_invalid_username_invalid_password(self):"""测试用户名无效,密码无效时的登录行为"""self._login('123', '123')self._verify_alert('用户不存在')self.driver.back()def _verify_alert(self, expected_alert_text):"""验证弹出警告框的文本内容"""BlogDriver.getScreenShot()try:alert = WebDriverWait(self.driver, 5).until(EC.alert_is_present())assert alert.text == expected_alert_text, f"Expected alert: '{expected_alert_text}', but got: '{alert.text}'"alert.accept()return Trueexcept (NoAlertPresentException, TimeoutException):return Falsedef test_logout_via_profile(self):"""测试通过个人资料页面登出"""self._login('zhangsan', '123456')self.driver.find_element(By.CSS_SELECTOR, '#submit').click()self.driver.get('http://117.72.87.234:8080/blog_list.html')WebDriverWait(self.driver,10).until(EC.presence_of_element_located((By.CSS_SELECTOR,'body > div.nav > a:nth-child(6)')))self.driver.find_element(By.CSS_SELECTOR,'body > div.nav > a:nth-child(6)').click()BlogDriver.getScreenShot()WebDriverWait(self.driver,10).until(EC.presence_of_element_located((By.CSS_SELECTOR,'body > div.container-login > div > h3')))self.driver.back()
BlogList.py
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from common.Utils import BlogDriverclass BlogList:"""博客列表页测试类"""url = ''driver = ''def __init__(self):"""初始化 WebDriver 并打开博客列表页"""self.url = 'http://117.72.87.234:8080/blog_list.html' # 博客列表页 URLself.driver = BlogDriver.driver # 获取 WebDriver 实例self.driver.get(self.url) # 打开博客列表页def test_account_name_display(self):"""测试用户名称是否正确显示"""BlogDriver.getScreenShot() # 截图用于调试user_name = self.driver.find_element(By.CSS_SELECTOR, 'body > div.container > div.left > div > h3').textassert user_name in ['zhangsan', 'lisi'], f"用户名不匹配,当前显示:{user_name}"def test_article_count_display(self):"""测试文章统计信息是否正确显示"""BlogDriver.getScreenShot()article_text = self.driver.find_element(By.CSS_SELECTOR, 'body > div.container > div.left > div > div:nth-child(4) > span:nth-child(1)').textassert article_text == '文章', f"页面上未找到'文章'文本,当前文本:{article_text}"def test_blog_title_exists(self):"""测试博客标题是否存在"""BlogDriver.getScreenShot()assert self.is_element_present(By.CSS_SELECTOR, 'body > div.container > div.right > div:nth-child(1) > div.title'), "博客标题未找到"def test_blog_date_exists(self):"""测试博客发布日期是否存在"""BlogDriver.getScreenShot()assert self.is_element_present(By.CSS_SELECTOR, 'body > div.container > div.right > div:nth-child(1) > div.date'), "博客发布日期未找到"def test_blog_content_exists(self):"""测试博客内容简介是否存在"""BlogDriver.getScreenShot()assert self.is_element_present(By.CSS_SELECTOR, "div.content.markdown-body.editormd-html-preview"), "博客内容简介未找到"def test_blog_button_exists(self):"""测试博客详情按钮是否存在"""BlogDriver.getScreenShot()assert self.is_element_present(By.CSS_SELECTOR, 'body > div.container > div.right > div:nth-child(1) > a'), "博客详情按钮未找到"def test_navigation_to_blog_editor(self):"""测试导航栏跳转到博客编辑页"""self.driver.find_element(By.CSS_SELECTOR, 'body > div.nav > a:nth-child(4)').click()try:WebDriverWait(self.driver, 5).until(EC.title_is('博客编辑页'))except:passassert self.driver.title == '博客编辑页', f"页面跳转失败,当前页面标题:{self.driver.title}"def test_logout_redirect_to_login_page(self):"""测试退出账号是否跳转到登录页"""self.driver.find_element(By.CSS_SELECTOR, 'body > div.nav > a:nth-child(5)').click()try:WebDriverWait(self.driver, 5).until(EC.title_is('博客登陆页'))except:passassert self.driver.title == '博客登陆页', f"退出登录失败,当前页面标题:{self.driver.title}"def is_element_present(self, by, selector):"""检查页面元素是否存在"""try:WebDriverWait(self.driver, 5).until(EC.presence_of_element_located((by, selector)))return Trueexcept:return False
BlogEdit.py
from common.Utils import BlogDriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as ECclass BlogEdit:url = ''driver = ''def __init__(self):"""初始化WebDriver并打开博客编辑页面"""self.url = 'http://117.72.87.234:8080/blog_edit.html'self.driver = BlogDriver.driverself.driver.get(self.url)def test_publish_redirect_list(self):"""测试发布博客后是否跳转到博客列表页"""# 输入博客标题self.driver.find_element(By.CSS_SELECTOR, '#title').send_keys('789')# 提交博客self.driver.find_element(By.CSS_SELECTOR, '#submit').click()BlogDriver.getScreenShot()# 等待页面跳转到博客列表页,并验证跳转是否成功try:WebDriverWait(self.driver, 10).until(EC.title_is('博客列表页'))except:pass# 确保页面标题为'博客列表页'assert self.driver.title == '博客列表页', f"页面标题错误,实际为:{self.driver.title}"
BlogDetail.py
import time
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.alert import Alert
from common.Utils import BlogDriver
from selenium.webdriver.common.by import Byclass BlogDetail:url = ''driver = ''def __init__(self):"""初始化 WebDriver 并打开博客详情页"""self.url = 'http://117.72.87.234:8080/blog_detail.html?blogId=17' #此处因为程序设计问题每次需要修改blogId的参数,需要注意self.driver = BlogDriver.driverself.driver.get(self.url)def test_current_user_can_edit(self):"""测试当前用户是否能编辑博客"""WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, 'body > div.container > div.right > div > div.operating > button:nth-child(1)')))self.driver.find_element(By.CSS_SELECTOR,'body > div.container > div.right > div > div.operating > button:nth-child(1)').click()BlogDriver.getScreenShot()# 确保页面跳转到博客编辑页assert self.driver.title == '博客编辑页', f"编辑页面标题错误:{self.driver.title}"def test_current_user_can_delete(self):"""测试当前用户是否能删除博客"""# 等待删除按钮可点击WebDriverWait(self.driver,10).until(EC.presence_of_element_located((By.CSS_SELECTOR,'body > div.container > div.right > div > div.operating > button:nth-child(2)')))self.driver.find_element(By.CSS_SELECTOR,'body > div.container > div.right > div > div.operating > button:nth-child(2)').click()# 等待弹出确认框alert = WebDriverWait(self.driver,5).until(EC.alert_is_present())assert alert.text == '确认删除这篇文章吗?', f"弹窗提示不匹配:{alert.text}"alert.accept()BlogDriver.getScreenShot()# 等待页面跳转至博客列表页WebDriverWait(self.driver, 10).until(EC.title_is('博客列表页'))assert self.driver.title == '博客列表页', f"页面标题错误:{self.driver.title}"def test_other_users_cannot_edit_delete(self):self.driver.get("http://117.72.87.234:8080/blog_detail.html?blogId=6") #此处是访问非当前用户的博客来进行功能的校验"""测试其他用户无法编辑或删除博客"""edit_buttons = self.driver.find_elements(By.CSS_SELECTOR,'body > div.container > div.right > div > div.operating > button:nth-child(1)')delete_buttons = self.driver.find_elements(By.CSS_SELECTOR,'body > div.container > div.right > div > div.operating > button:nth-child(2)')BlogDriver.getScreenShot()# 确保没有编辑和删除按钮assert not edit_buttons, "其他用户能看到编辑按钮"assert not delete_buttons, "其他用户能看到删除按钮"
BlogCancellation.py
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from common.Utils import BlogDriverclass BlogCancellation:url = ''driver = ''def __init__(self):"""初始化 WebDriver 并打开博客列表页"""self.url = 'http://117.72.87.234:8080/blog_list.html'self.driver = BlogDriver.driverself.driver.get(self.url)def test_logout_success(self):"""测试用户注销后是否正确跳转回登录页,并限制访问受保护页面"""# 点击注销按钮self.driver.find_element(By.CSS_SELECTOR, 'body > div.nav > a:nth-child(5)').click()BlogDriver.getScreenShot()# 等待页面跳转到登录页(10秒内出现登录标题)WebDriverWait(self.driver,10).until(EC.presence_of_element_located((By.CSS_SELECTOR,'body > div.container-login > div > h3')))# 确保用户名和密码框为空assert self.driver.find_element(By.CSS_SELECTOR,'#username').get_attribute('value') == ''assert self.driver.find_element(By.CSS_SELECTOR,'#password').get_attribute('value') == ''# 测试访问受保护的页面,确保被重定向到登录页protected_pages = ["http://117.72.87.234:8080/blog_list.html",]for page in protected_pages:self.driver.get(page)WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, 'body > div.container-login > div > h3')))# 额外检查:尝试编辑博客,确保未登录状态下不能提交self.driver.get("http://117.72.87.234:8080/blog_edit.html")try:self.driver.find_element(By.CSS_SELECTOR, '#title').send_keys('1')self.driver.find_element(By.CSS_SELECTOR, '#submit').click()WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR, 'body > div.container-login > div > h3')))except:print("未登录用户无法提交博客,测试通过")
5.测试内容外的BUG分析
1.该项目中未提供注册功能,只能通过直接操作数据库的方式来增删用户
2.在未登录状态下直接通过url访问文章详情页时发生401错误,原因为代码设计时未能在详情页调用接口被JWT令牌拦截后进行相应处理而造成了该错误
项目及自动化测试脚本源码:MindVault: 该项目搭建的是一个文章分享阅读的平台