前段时间给手头一个博客系统补了自动化测试,用 Python + Selenium。套路不复杂,但有几个点踩过坑之后觉得还是要记一下。

测试范围
系统不大,核心就是登录、博客列表、博客详情这几个高频操作,所以没搞全覆盖。用例按'正常路径 + 一个典型异常'来设计,够用,也不至于写一堆一年跑不了两次的脚本。
脚本结构
项目结构大致这样:
common/Utils.py:初始化 ChromeDriver、截图cases/BlogLogin.py:登录(成功 + 失败)cases/BlogList.py:列表页检查与跳转cases/BlogDetail.py:详情页元素验证cases/RunCases.py:用例串联入口
通用工具
ChromeDriver 用 webdriver-manager 自动管理,省得手动下载。截图按日期分目录,文件名带用例名和时间戳,回看的时候容易定位。
import datetime
import os.path
import sys
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
class Driver:
driver = ""
def __init__(self):
options = webdriver.ChromeOptions()
# options.page_load_strategy = 'eager'
self.driver = webdriver.Chrome(
service=Service(ChromeDriverManager().install()),
options=options
)
def getScreenShot(self):
dirname = datetime.datetime.now().strftime('%Y-%m-%d')
if not os.path.exists("../images/" + dirname):
os.mkdir("../images/" + dirname)
filename = (
sys._getframe().f_back.f_code.co_name
+ "-"
+ datetime.datetime.now().strftime('%Y-%m-%d-%H%M%S')
+ ".png"
)
self.driver.save_screenshot(f'../images/{dirname}/' + filename)
BlogDriver = Driver()
登录模块
两个用例:admin 正确密码登录成功,admin 错误密码登录失败并断言提示文案。元素定位用 CSS 选择器,直接又稳定。登录成功后通过头像元素确认登录态,失败则比对 body 文本。每步都截图,哪怕断言失败也能翻图看现场。
import time
from selenium.webdriver.common.by import By
from common.Utils import BlogDriver
class BlogLogin:
def __init__(self):
self.url = "http://192.168.47.135:8653/blog_system/blog_login.html"
self.driver = BlogDriver.driver
self.driver.get(self.url)
def loginSucTest(self):
time.sleep(2)
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()
# 登录后应有头像元素
self.driver.find_element(
By.CSS_SELECTOR, "body > div.container > div.left > div > img"
)
BlogDriver.getScreenShot()
self.driver.back()
def loginFailTest(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("admin")
self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys("111")
self.driver.find_element(By.CSS_SELECTOR, "#submit").click()
expect = "用户名或密码错误!"
actual = self.driver.find_element(By.CSS_SELECTOR, "body").text
print(actual)
BlogDriver.getScreenShot()
assert expect == actual
self.driver.back()
列表页
列表页测试需要先处于登录态,所以它得在登录用例之后执行。检查头像存在、第一篇文章链接可点击、文章数量大于 10(业务要求),然后点击第一篇文章,断言跳转到详情页。
from selenium.webdriver.common.by import By
from common.Utils import BlogDriver
class BlogList:
def __init__(self):
self.driver = BlogDriver.driver
self.url = "http://192.168.47.135:8653/blog_system/blog_list.html"
self.driver.get(self.url)
def ListTest(self):
self.driver.find_element(
By.CSS_SELECTOR, "body > div.container > div.left > div > img"
)
self.driver.find_element(
By.CSS_SELECTOR,
"body > div.container > div.right > div:nth-child(1) > a",
)
articles = self.driver.find_elements(
By.CSS_SELECTOR, "body > div.container > div.right > div"
)
assert len(articles) > 10
self.driver.find_element(
By.CSS_SELECTOR,
"body > div.container > div.right > div:nth-child(1) > a",
).click()
title = self.driver.title
assert title == "博客详情页"
BlogDriver.getScreenShot()
详情页
详情页测试做了点小兼容:如果当前不在列表页,就手动带 blogId=15 跳过去。然后简单验一下标题、日期、正文三个元素是否存在。
from selenium.webdriver.common.by import By
from common.Utils import BlogDriver
class BlogDetail:
def __init__(self):
self.url = "http://192.168.47.135:8653/blog_system/blog_detail.html?blogId=15"
self.driver = BlogDriver.driver
title = self.driver.title
if not title == "博客列表页":
self.driver.get(self.url)
def DetailCheck(self):
BlogDriver.getScreenShot()
self.driver.find_element(
By.CSS_SELECTOR, "body > div.container > div.right > div > h3"
)
self.driver.find_element(
By.CSS_SELECTOR, "body > div.container > div.right > div > div.date"
)
self.driver.find_element(By.CSS_SELECTOR, "#content")
用例串联
入口脚本按登录 → 列表 → 详情的顺序跑,跑完关浏览器。注意导入路径,我这里实际目录是 cases,所以用的 from cases import ...。
from common.Utils import BlogDriver
from cases import BlogLogin
from cases import BlogList
from cases import BlogDetail
if __name__ == "__main__":
BlogLogin.BlogLogin().loginSucTest()
BlogList.BlogList().ListTest()
BlogDetail.BlogDetail().DetailCheck()
BlogDriver.driver.quit()
报告收尾
自动化跑完不看报告等于白跑。我一般会整理一个简单的测试报告,把截图、断言结果、执行时间列出来。下面这张是当时用的报告模板。


这版实现不花哨,但够直接——没有框架黑盒,每一步都看得见,出问题也好排查。后面要加用例或改成 pytest 组织也很自然。如果你也在给内部工具写 Selenium 测试,这种'先跑通,再优化'的节奏可能会省不少时间。

