1.项目背景
在信息爆炸的时代,个体的思想、经验和知识如同星辰般散落在浩瀚的网络宇宙中,缺乏一个能够让其有序闪耀、相互关联的专属空间。现有的博客平台或过于繁杂,或过于封闭,难以满足现代创作者对于简洁写作、深度思考与知识互联的核心需求。
LogiVerse 应运而生。
“LogiVerse”一词,源自 “Log”(日志)与 “Universe”(宇宙)的结合。它精准地诠释了本项目的愿景:为每一位创作者打造一个包罗万象、互联互通的个人思想宇宙。
这不仅仅是一个博客系统。在 LogiVerse 中,每一篇文章不再是孤立的知识点,而是构成了你个人知识星系的一颗星球。它们通过标签、分类和内在的逻辑相互关联,形成一张独特的、属于你个人的思维图谱。创作者可以在此:
-
专注地记录:享受极简主义的写作体验,摆脱冗余功能的干扰,让思绪自由流淌。
-
系统地管理:利用强大的标签系统和结构化的分类,将零散的灵感系统化为知识库。
-
智慧地连接:发现观点与观点之间的内在联系,从而激发新的思考与创作灵感。
-
自信地展示:通过清晰、现代且高度可定制的界面,向世界展示你独一无二的思想宇宙。
LogiVerse 采用先进的技术栈构建,致力于在提供强大功能的同时,保持代码的优雅与系统的稳定。它既是开发者一个练手的项目,更是我们对未来知识管理方式的一次探索和实践。
加入 LogiVerse,开始绘制你的星辰大海。
2.项目简介
博客登录功能:输入账号和密码,还有登录按钮。
博客列表页功能:博客首页,会显示出每篇博客的标题、时间、部分内容和查看全文按钮。
博客详情页功能:查看该博客详情内容,比如文章标题、发布时间,还有详细的内容。
博客编辑页功能:有标题输入框、内容输入框、发布文章按钮。
3.测试计划
| 功能 | 后端开发 | 前端开发 | 提测日期 | 测试 | 测试日期 | 测试结果 |
| 登录界面 | 张三 | 李四 | 8.8 | 赵六 | 8.10 | 测试通过 |
| 博客首页 | 张三 | 王五 | 8.8 | 赵六 | 8.11 | 测试通过 |
| 博客详情页 | 张三 | 李四 | 8.9 | 赵六 | 8.12 | 测试通过 |
| 博客编辑页 | 张三 | 李四 | 8.10 | 赵六 | 8.13 | 测试通过 |
| 回顾测试 | 赵六 | 8.17 | 测试通过 |
4.测试工具
Postman、jemeter
5.设计到的测试动作/类型
功能测试、性能测试、自动化测试。
功能测试:覆盖博客登录页面、博客列表页、博客详情页、博客编辑页。发现了一个小bug
性能测试:覆盖博客登录页面、博客列表页、博客详情页、博客编辑页
自动化测试:覆盖博客登录页面、博客列表页、博客详情页、博客编辑页。登录页面测试了正常登录和异常登录。博客列表页测试了成功登录后能正常跳转。博客详情页测试了在列表页能不能点击查看全文按钮正常进入浏览博客全部内容。博客编辑页测试了如果填写好了标题和内容能不能正常发布博客
6.功能测试
思维导图

博客登录界面测试:
(1)输入网址进入登录界面

(2)输入正确的账号和密码,点击登录按钮、
预期效果:正常登录进入博客首页。


(3)输入错误的账号和密码,点击登录
预期结果:异常登录原因用户不存在,弹出警告弹窗。


(4)错误的账号和正确的密码
预期结果:异常登录原因用户不存在,弹出警告弹窗。


(5)正确的账户和错误的密码
预期结果:异常登录原因密码错误,弹出警告弹窗。


博客首页测试
(1)登录状态下访问会有个人信息模块和博客列表模块
预期结果:出现个人信息模块,以及发布过的文章。

(2)未登录状态下访问

博客详情页测试
(1)登录状态下访问详情页
预期结果:出现个人信息模块,其中还有博客的标题、博客的时间、博客的内容、
(2)未登录状态下访问详情页
预期结果:跳转回登录界面。

博客编辑页测试
(1)登录状态下访问编辑页
预期结果:出现标题输入框、编辑工具区域、文本输入框以及发布文章按钮。

(2)未登录状态下访问编辑页
预期结果:跳转回登录界面。
还是能正常访问这个界面,但是点击任何按钮都是会跳转回登录界面。

7.使用selenium进行自动化测试
想要进行Web自动化首先要安装好两个工具:
(1)驱动管理工具Webdriver-manager:每次都要浏览器更新都要下载新的驱动,整个流程非常冗余。
命令:pip install webdriver-manager
(2)selenium工具:编写自动化脚本需要用到该工具,是一个Web自动化测试工具,包含了丰富的接口给用户进行Web自动化测试
pip install selenium==4.0.0
(3)最后我们需要用到Pycharm集成开发工具来编写Python自动化脚本。
注意:检查Python解释器中是否包含webdriver-manaager和selenium。
根据思维导图先搭框架:

common文件夹中的Util.py文件代码:
import os
import sys
import datetime
from selenium import webdriver
# from selenium.webdriver.support.wait import WebDriverWait
from webdriver_manager.microsoft import EdgeChromiumDriverManager
from selenium.webdriver.edge.service import Service
class Driver:
driver = ""
def __init__(self):
options = webdriver.EdgeOptions()
self.driver = webdriver.Edge(service=Service(EdgeChromiumDriverManager().install()),options=options)
#添加一个隐式等待,预防代码执行速度比页面渲染加载快,报错
self.driver.implicitly_wait(2)#隐式等待两秒,隐式等待的生命周期是整个查找过程
#截图函数
def getScreeShot(self):
#首先在上一级路径创建一个文件夹,命名为:images+当天的年月日
#创建之前先看一下,文件夹是否存在
driname = datetime.datetime.now().strftime("%Y-%m-%d")
if not os.path.exists("../images/"+driname):
#如果不存在,就创建文件夹
os.mkdir("../images/"+driname)
#截图的图片名称要加上调用是哪个函数调用这个截图函数的
#图片名称:调用的方法-2025-04-08-172534.png
filename =sys._getframe().f_back.f_code.co_name+"-"+datetime.datetime.now().strftime("%Y-%m-%d-%H%M%S")+".png"
#利用驱动对象调用截图函数
# 图片路径: ../images/2025-04-08/调用的方法-2025-04-08-172534.png
self.driver.save_screenshot("../images/"+driname+"/"+filename)
BlogDriver = Driver() #使用Driver类创建一个类驱动对象,其他函数想要使用这个驱动就得使用BlogDriver调用里面的驱动成员driver
类的构造函数__init__(self)中driver的解释:
浏览器就像一台跑车,想要启动就需要有驱动,所以我们要先创建一个驱动(在webdriver-manager里),这个驱动作为Service的参数打开谷歌浏览器。options是配置选项不用但是可以先留着.
博客登录的自动化测试
BlogLoginTest测试文件:
import time
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from common.Utils import BlogDriver
# 测试博客登录页面
class BlogLogin:
url=""
driver = ""
def __init__(self):
self.url = "http://8.137.19.140:9090/blog_login.html"
self.driver = BlogDriver.driver #赋值给当前类里面的成员
self.driver.get(self.url)
# 成功登录的测试用例
def LoginSucTest(self):
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("zhangsan")
self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys("123456")
self.driver.find_element(By.CSS_SELECTOR, "#submit").click()
# 能够找到博客首页用户的名称,说明登录成功,否则登录失败
self.driver.find_element(By.CSS_SELECTOR, "body > div.container > div.left > div > h3")
# 添加屏幕截图
BlogDriver.getScreeShot()
# 异常登录的测试用例
def LoginFailTest(self):
# 账号错误
# 连续出现多次send_keys会出现拼接,而不是替换。若要替换需要先clear
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("admin")
# 错误密码
self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys("123")
self.driver.find_element(By.CSS_SELECTOR, "#submit").click()
# 检查是否登录失败
# 切换到窗口
WebDriverWait(self.driver, 10).until(EC.alert_is_present())
alert = self.driver.switch_to.alert
# 确定
alert.accept()
# 添加屏幕截图
BlogDriver.getScreeShot() #类对象调用的成员方法
# 断言检测一下是否符合预期
# assert actual == ("用户名或者密码")
login = BlogLogin()
login.LoginSucTest()
tests文件夹中RunTest.py文件代码:
from common.Utils import BlogDriver
from tests import BlogLogin
if __name__ == "__main__":
# BlogLogin.BlogLogin().LoginFailTest() #从BlogLogin文件里创建一个类对象,再去调用方法
BlogLogin.BlogLogin().LoginSucTest()
# 执行浏览器的退出
BlogDriver.driver.quit()
(1)from tests import xxx
从tests文件夹里引入xxx文件。
(2)if __name__ == "__main__":
项目中程序的执行入口,顺序结构依次调用类对象里面的方法。
(3)以BlogLogin.BlogLogin().LoginFailTest() 为例
意思是从BlogLogin文件里创建一个类对象,再去调用方法。
我们还可以继续添加测试用:
#输入错误的账号,错误的密码
self.driver.find_element(By.CSS_SELECTOR, "#username").send_keys("wangwu")
self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys("123")
# 输入错误的账号,正常的密码
self.driver.find_element(By.CSS_SELECTOR, "#username").send_keys("wangwu")
self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys("123456")
# 输入正确的账号,错误的密码
self.driver.find_element(By.CSS_SELECTOR, "#username").send_keys("zhangsan")
self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys("123")
# 不输入账号,不输入密码
self.driver.find_element(By.CSS_SELECTOR, "#username").send_keys("")
self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys("")
博客列表页自动化测试
博客列表页面的BlogListTest测试文件:
from selenium.webdriver.common.by import By
from common.Utils import BlogDriver
#博客首页测试用例
class BlogList:
url=""
driver=""
def __init__(self):
self.url="http://8.137.19.140:9090/blog_list.html"
self.driver = BlogDriver.driver
self.driver.get(self.url)
# 测试首页(登录情况下)
def ListTestByLogin(self):
# 测试博客标题是否存在
self.driver.find_element(By.CSS_SELECTOR, "body > div.container > div.right > div:nth-child(1) > div.title")
# 测试博客内容是否存在
self.driver.find_element(By.CSS_SELECTOR, "body > div.container > div.right > div:nth-child(1) > div.desc")
# 测试博客按钮是否存在
self.driver.find_element(By.CSS_SELECTOR, "body > div.container > div.right > div:nth-child(1) > a")
# 个人信息-检查名称是否存在
self.driver.find_element(By.CSS_SELECTOR, "body > div.container > div.left > div > h3")
# 添加屏幕截图
BlogDriver.getScreeShot()
# 测试首页(未登录情况下)
def ListTestByNotLogin(self):
# 未登录情况下访问会直接跳转回登录界面
# 登录成功后我们先退出登录
self.driver.find_element(By.CSS_SELECTOR, "body > div.nav > a:nth-child(6)").click()
# 未登录状态下直接进入博客首页
self.driver.get("http://8.137.19.140:9090/blog_list.html")
# 测试用户账号输入框是否存在
self.driver.find_element(By.CSS_SELECTOR, "#username")
# 测试用户密码输入框是否存在
self.driver.find_element(By.CSS_SELECTOR, "#password")
# 测试登录按钮是否存在
self.driver.find_element(By.CSS_SELECTOR, "#submit")
# 添加屏幕截图
BlogDriver.getScreeShot()
RunTest.py文件代码:
from common.Utils import BlogDriver
from tests import BlogLogin
from tests import BlogList
if __name__ == "__main__":
# BlogLogin.BlogLogin().LoginFailTest() #从BlogLogin文件里创建一个类对象,再去调用方法
BlogLogin.BlogLogin().LoginSucTest()
# 登录成功之后就可以调用博客首页测试的用例(登录状态)
BlogList.BlogList().ListTestByLogin()
# 继续调用博客首页测试的用例(未登录状态)
BlogList.BlogList().ListTestByNotLogin()
# 执行浏览器的退出
BlogDriver.driver.quit()
有截图能看到和我们预期的结果一样
博客详情页的自动化测试
BlogDetailTest.py测试文件
import time
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 BlogDriver
# 测试博客详情页
class BlogDetail:
url=""
driver=""
def __init__(self):
self.url="http://8.137.19.140:9090/blog_detail.html?blogId=165657"#不是固定的
self.driver = BlogDriver.driver
self.driver.get(self.url)
# 登录状态下博客详情页的测试
def DetailTestBylogin(self):
# 检查博客标题
self.driver.find_element(By.CSS_SELECTOR,"body > div.container > div.right > div > div.title")
# 检查博客发布时间
self.driver.find_element(By.CSS_SELECTOR,"body > div.container > div.right > div > div.date")
# 屏幕截图
BlogDriver.getScreeShot()
# 未登录状态下博客详情页的测试
def DetailTestByNotlogin(self):
# 先注销
self.driver.find_element(By.CSS_SELECTOR, "body > div.nav > a:nth-child(6)").click()
# 未登录会跳转到登录界面
# 测试用户账号输入框是否存在
self.driver.find_element(By.CSS_SELECTOR, "#username")
# 测试用户密码输入框是否存在
self.driver.find_element(By.CSS_SELECTOR, "#password")
# 测试登录按钮是否存在
self.driver.find_element(By.CSS_SELECTOR, "#submit")
# 添加屏幕截图
BlogDriver.getScreeShot()
def DeleteBlogLoginDetailTest(self):
self.driver.find_element(By.CSS_SELECTOR, "body > div.container > div.right > div > div.operating > button:nth-child(2)").click()
# alert_is_present()检查是否出现弹窗
WebDriverWait(self.driver, 1).until(EC.alert_is_present())
# 切换到窗口
alert = self.driver.switch_to.alert
# 找到弹窗里面的内容
actual = alert.text
print(actual)
assert actual == "确定删除?"
# 确定
alert.accept()
time.sleep(1)
# 添加屏幕截图
BlogDriver.getScreeShot()
测试删除的时候url里面的连接试一次性的。
博客删除功能测试RunTest测试文件:
from common.Utils import BlogDriver
from tests import BlogLogin
from tests import BlogList
from tests import BlogDetail
from tests import BlogEdit
if __name__ == "__main__":
# BlogLogin.BlogLogin().LoginFailTest() #从BlogLogin文件里创建一个类对象,再去调用方法
BlogLogin.BlogLogin().LoginSucTest()
# 测试登录状态下的博客详情页
BlogDetail.BlogDetail().DetailTestBylogin()
# 测试删除博客按钮
BlogDetail.BlogDetail().DeleteBlogLoginDetailTest()
# 执行浏览器的退出
BlogDriver.driver.quit()
先测试在登录状态下能不能正常访问博客详情页:

可以看到登录的时候博客在列表页是存在的:

删除后博客已经在列表页消失了:

博客编辑页的自动化测试
博客编辑页面的BlogEditTest测试文件:
# 测试博客编辑页面
import time
from selenium.webdriver.common.by import By
from common.Utils import BlogDriver
class BlogEdit:
url=""
driver=""
def __init__(self):
self.url="http://8.137.19.140:9090/blog_edit.html"
self.driver = BlogDriver.driver
self.driver.get(self.url)
# 正确发布博客(登录状态下)
def EditSucTestByLogin(self):
self.driver.find_element(By.CSS_SELECTOR, "#title").send_keys("自动化测试创建")
#通过点击菜单栏达到编辑区域不为空的目的
# self.driver.find_element(By.CSS_SELECTOR, "#editor > div.editormd-toolbar > div > ul > li:nth-child(21) > a > i").click()
# 直接点击发布按钮来发布博客
self.driver.find_element(By.CSS_SELECTOR, "#submit").click()
# 点击完成后出现跳转页面,页面跳转需要时间,代码运行速度比页面渲染速度快,导致元素找不到,因此可以添加等待
#添加隐式等待和显示等待都可以
# 隐式等待:创建浏览器对象之后就可以加上,因为隐式等待的作用域在driver整个生命周期
# 显示等待:可以作用在当前代码中
actual = self.driver.find_element(By.CSS_SELECTOR, "body > div.container > div.right > div:nth-child(13) > div.title").text
assert actual == "自动化测试创建"
# 屏幕截图
BlogDriver.getScreeShot()
def NotBlogLoginEditTest(self): # 未登录下
# 未登录下是找不到标题输入框输入标题
self.driver.find_element(By.CSS_SELECTOR, "#title").send_keys("博客系统测试编辑页面")
博客编辑页面的RunTest测试文件:
from common.Utils import BlogDriver
from tests import BlogLogin
from tests import BlogList
from tests import BlogDetail
from tests import BlogEdit
if __name__ == "__main__":
# BlogLogin.BlogLogin().LoginFailTest() #从BlogLogin文件里创建一个类对象,再去调用方法
BlogLogin.BlogLogin().LoginSucTest()
# 博客编辑页面
BlogEdit.BlogEdit().EditSucTestByLogin()
# 执行浏览器的退出
BlogDriver.driver.quit()
没有写博客之前的首页:

运行编辑博客之后的首页

8.Bug简述
本次项目测试发现了0个bug,有多少个崩溃级别(P0)的bug,多少个严重级别(P1)的bug,多少个一般级别(P2/P3)bug
|
bug标题 | 报告人(提bug的人) | 是否修复 |
| 放上bug连接 | 赵六 | 修改完成 |
| 放上bug连接 | 赵六 | 修改完成 |
9.使用jmeter对博客系统进行性能测试
我们先要知道常见的性能测试指标:
并发数:后端服务器层面看,指的是web服务器在一段时间内处理浏览器请求而建立的http连接数或者生成的处理线程数。
吞吐量:单位时间内处理的并发数。直接体现软件系统负载承载力,吞吐量越高,系统承受的并发越多,性能越好。
吞吐量分类:
(1)按照请求数量:TPS(transaction per second)和QBS(Query per second)
TPS(transaction per second):每秒处理事务数,用于衡量系统在一定时间内能够处理的事务数。
计算公式:总的请求成功的事务数 / 总的运行时间
QBS(Query per second):每秒查询率
开始用jemeter测试之前要安装好jmeter。(csdn上有这里就不做过多描述)
启动jmeter。(具体教程看另外一篇博客,这里展示结果)
想要测试性能接口要能跑通。
这里先设置HTTP请求默认值(配置元件):这里有的内容后边文件里都不用写了。
测试登录页面接口
先在Postman里能运行:

点击运行

会用到JSON提取器(<-后置处理器)和JSON断言(<-断言)


查看结果树:
![]()

后边操作涉及到了信息头管理器

获取到的token是登录里JSON提取器的内容

测试博客列表页接口
先在Postman里跑通:

这里会用到用户的登录凭证,在那里看呢?

HTTP请求文件

查看结果树:
![]()

测试博客用户个人信息接口
先在Postman上跑通:

jmeter上的HTTP请求文件:

查看结果树:
![]()

测试博客详情页接口
现在Postman上跑通:

jmeter上的文件:
查看结果树:
![]()

测试添加博客接口:
先在Postman上跑通:
我们再博客上发布成功之后(Ctri+Shift+i)会出现add接口


add接口参数是JSON格式的请求参数

访问网站再首页出现我们用Postman请求创建的博客

jmeter文件

需要用到HTTP信息头管理(<-配置元件)
查看结果树:
![]()

测试添加JSON断言:

如果不满足要求结果树会出现断言报错:
真正的性能测试:
我们要添加插件(jmeter管理插件工具 )
https://jmeter-plugins.org/install/Install/

看jmeter右上角的小蝴蝶

点击

下载![]()
![]()
准备工作做好之后:添加新的线程组
梯度压测线程组





报告是不够的,所以我们还要关注在线程执行过程中响应时间和吞吐量(活跃线程状态也可以加)



响应时间和吞吐量成反比。
从聚合报告中我们可以看到列表页响应时间是最久的。有个线程异常了没有得到响应。

在应响应时间图的y轴也是66秒才结束。
测试完成后要给开发同学看。
生成性能测试报告
⽣成性能测试报告的命令:在终端执行命令
Jmeter -n -t 脚本⽂件 -l ⽇志⽂件 -e -o ⽬录
-n : ⽆图形化运⾏
-t : 被运⾏的脚本
-l : 将运⾏信息写⼊⽇志⽂件,后缀为 jtl 的⽇志⽂件
-e : ⽣成测试报告
-o : 指定报告输出⽬录
例子:jmeter -n -t 第一个性能测试案例.jmx -l first -e -o ./first/.



746

被折叠的 条评论
为什么被折叠?



