一.概念
⾃动化的主要⽬的就是⽤来进行回归测试。
即软件有多个版本需要进行功能的整体回归 。 为了避免新增功能影响到历史的功能需要进⾏功能的回归。
一定程度上。测试工作不仅仅包含测试执行阶段。
测试主要由人工进行,自动化测试知识辅助。
1.自动化


然后实际上在企业中,⾃动化往往是"冰淇淋蛋筒反模式"

- 越往底层(单元 / API)投入自动化,投入产出比越高。
- 越往上层(UI / 手动)投入,维护成本和时间成本越高,效率越低。
- 理想的测试策略是:单元测试 + API 自动化为主,UI 自动化为辅,手动测试聚焦探索性场景。
- 把自动化重心放在单元测试和 API 测试上,它们的维护成本低、执行效率高,能快速提供反馈。
- UI 自动化仅用于核心业务流程的回归,不要过度覆盖,避免维护成本失控。
- 手动测试专注于探索性测试、用户体验和边缘场景,不要把时间浪费在重复的回归用例上。
避免蛋筒模式的核心,就是让自动化测试 "下沉":单元测试打底,接口测试为主,UI 自动化为辅,手动测试做补充。这样既能保证产品质量,又能让自动化投入获得最高回报。
2.web自动化测试

程序想要打开web浏览器就需要安装web驱动(WebDriver),WebDriver以本地化⽅式驱动浏览器


项目底部控制台

3.selenium
Selenium 是网页自动化工具,用来代码操控浏览器,模拟人手动上网操作
编写自动化脚本需要selenium工具



当出现这些 代表配置好了
ctrl+Shift+i进入开发者模式

然后按小箭头 变蓝后可以指哪就得到其id
python
#编写自动化脚本
import time
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from webdriver_manager.chrome import ChromeDriverManager
# 1.打开浏览器 驱动-driver
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))
time.sleep(2)
# 2.输入百度网址http://www.sogou.com(一定要完整)
driver.get("http://www.sogou.com")
time.sleep(2)
# 3.找到输入框 输入关键词"杨幂"
driver.execute_script("document.getElementById('query').value='杨幂'")
# 4.点击"百度一下"按钮进行查找
driver.execute_script("document.getElementById('stb').click()")
time.sleep(2)
# 5.关闭浏览器
driver.quit()
ChromeDriverManager().install()
- driver.execute_script :执行 JS 代码(最稳定,永远不报错)
- document:整个网页
- getElementById('query') :找到 id 为 query 的搜索框
- value=' 杨幂' :给输入框赋值 = 杨幂
- 每个 Chrome 版本,必须有一个对应的小驱动程序,否则代码控制不了浏览器。可以管理:
selenium+驱动+浏览器的⼯作原理



二.常用函数
1.查找




对于同类型:

>叫「直接子元素选择器」- 作用:只选父元素的 "亲儿子",不选 "孙子" 或更后面的后代
- 大白话:
#hotsearch-content-wrapper > li→ 只找这个div下面第一层直接的li- 如果
li被包在别的标签里(比如div > ul > li),用>就找不到了



| 符号 | 含义 | 大白话 |
|---|---|---|
/ |
只找下一层的直接子节点 | 必须是 "亲儿子",不能跳层 |
// |
找所有层级的后代节点 | 不管中间隔了多少层,只要在里面就行 |


| 定位方式 | 亲儿子(直接相邻) | 所有后代(可以跨层) |
|---|---|---|
| CSS | 父元素 > 子元素 |
父元素 子元素(中间一个空格) |
| XPath | 父节点/子节点 |
父节点//子节点 |
2.操作





| 写法 | 清除用什么 | 可以用 clear () 吗? |
|---|---|---|
| Selenium 标准 | .clear() |
✅ 可以 |
| JavaScript | .value = ''(置空) |
❌ 不可以 |


(断言 = 代码里的 "检查点" 作用:判断某个条件是否成立,不成立就直接报错,终止程序)

元素属性值!=文本信息




3.窗口

点击图片 获取新的网页的title和url


每个浏览器窗口都有唯一的属性句柄(handle) 通过handle来切换


(逻辑:遍历所有标签页,找到和原来句柄不一样的那个(也就是新打开的标签),然后用 driver.switch_to.window() 把 driver 的视角切换过去)






strftime:格式化时间


4.弹窗



提示弹窗:

5.等待










(由于方法的参数只有一个 所以需要用括号将By...和'...'组合成一个元组)

注意:



6.浏览器导航


7.文件上传



8.浏览器参数设置




三.实战

1.编写测试用例


2.参考测试用例 编写自动化测试脚本



Utils:
python
# 创建一个浏览器对象
import datetime
import os
import sys
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager
class Driver:
# 相当于给这个工具预留一个放浏览器的位置
driver = ""
# 构造方法:只要这个工具一被创建,就自动执行这里面的代码
def __init__(self):
options = webdriver.ChromeOptions()
self.driver=webdriver.Chrome(service=Service(ChromeDriverManager().install()),
options=options)
# 创建屏幕截图
def getScreenshot(self):
# 图片文件名称:./2026-05-25-173456.png
# 图片路径:../images/调用方法-2026-05-25/2026-05-25-173456.png
dirname=datetime.datetime.now().strftime("%Y-%m-%d")
# 判断dirname文件夹是否存在 不存在则创建
if not os.path.exists("../images/"+dirname):
os.makedirs("../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("../images/"+dirname+"/"+filename)
BlogDriver = Driver()

登录页面:
python
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from common1.Utils import BlogDriver
from selenium.webdriver.support import expected_conditions as EC
# 测试博客登录页面
class BlogLogin:
url=""
driver=""
def __init__(self):
self.url="http://47.108.157.13:8090/blog_login.html"
self.driver=BlogDriver.driver
self.driver.get(self.url)
# 成功登录的测试用例
def LoginSucTest(self):
self.driver.implicitly_wait(5)
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()
# 能够找到博客首页的昵称 说明登陆成功
success_elem=self.driver.find_element(By.CSS_SELECTOR,"body > div.container > div.left > div > h3")
BlogDriver.getScreenshot()
assert success_elem.is_displayed(),"登录失败 未找到首页元素"
print("✅ 博客登录成功测试用例执行通过")
self.driver.back()
# 失败登录的测试用例
def LoginFail_UserError(self):
self.driver.implicitly_wait(5)
self.driver.find_element(By.CSS_SELECTOR,"#username").clear()
self.driver.find_element(By.CSS_SELECTOR,"#username").send_keys("zhangsann")
self.driver.find_element(By.CSS_SELECTOR,"#password").clear()
self.driver.find_element(By.CSS_SELECTOR,"#password").send_keys("123456")
self.driver.find_element(By.CSS_SELECTOR,"#submit").click()
try:
# 关键修复:等待弹窗出现!!!隐式等待等不了弹窗
WebDriverWait(self.driver, 3).until(EC.alert_is_present())
# 切换到弹窗
alert = self.driver.switch_to.alert
# 获取弹窗文字
msg = alert.text
print("弹窗提示:", msg)
# 关闭弹窗
alert.accept()
# 关闭alert后才能截屏!!! 但此时也就没必要
# BlogDriver.getScreenshot()
# 能走到这里,说明弹窗出现了 → 测试正确
print("✅ 用户名错误登录失败弹窗正常出现,测试通过!")
except:
# 没有弹窗 → 测试失败
print("❌ 未出现弹窗,测试失败!")
assert False
def LoginFail_PassError(self):
self.driver.implicitly_wait(5)
self.driver.find_element(By.CSS_SELECTOR,"#username").clear()
self.driver.find_element(By.CSS_SELECTOR,"#username").send_keys("zhangsan")
self.driver.find_element(By.CSS_SELECTOR,"#password").clear()
self.driver.find_element(By.CSS_SELECTOR,"#password").send_keys("123")
self.driver.find_element(By.CSS_SELECTOR,"#submit").click()
try:
WebDriverWait(self.driver, 3).until(EC.alert_is_present())
alert = self.driver.switch_to.alert
msg = alert.text
print("弹窗提示"+msg)
alert.accept()
print("✅ 密码错误登录失败弹窗正常出现,测试通过!")
except:
print("❌ 未出现弹窗,测试失败!")
assert False
def LoginFail_UAPError(self):
self.driver.implicitly_wait(5)
self.driver.find_element(By.CSS_SELECTOR,"#username").clear()
self.driver.find_element(By.CSS_SELECTOR,"#username").send_keys("zhangggsan")
self.driver.find_element(By.CSS_SELECTOR,"#password").clear()
self.driver.find_element(By.CSS_SELECTOR,"#password").send_keys("123")
self.driver.find_element(By.CSS_SELECTOR,"#submit").click()
try:
WebDriverWait(self.driver, 3).until(EC.alert_is_present())
alert = self.driver.switch_to.alert
msg = alert.text
print("弹窗提示"+msg)
alert.accept()
print("✅ 用户名 + 密码错误登录失败弹窗正常出现,测试通过!")
except:
print("❌ 未出现弹窗,测试失败!")
assert False
大量重复代码 可优化:
python
import time
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from common1.Utils import BlogDriver
from selenium.webdriver.support import expected_conditions as EC
# 测试博客登录页面
class BlogLogin:
url = ""
driver = ""
def __init__(self):
self.url = "http://47.108.157.13:8090/blog_login.html"
self.driver = BlogDriver.driver
self.driver.get(self.url)
# ==========================
# 【提取公共方法】输入账号密码 + 清空 + 登录
# ==========================
def input_login(self, username, password):
self.driver.implicitly_wait(5)
self.driver.find_element(By.CSS_SELECTOR, "#username").clear()
self.driver.find_element(By.CSS_SELECTOR, "#username").send_keys(username)
self.driver.find_element(By.CSS_SELECTOR, "#password").clear()
self.driver.find_element(By.CSS_SELECTOR, "#password").send_keys(password)
self.driver.find_element(By.CSS_SELECTOR, "#submit").click()
# ==========================
# 【提取公共方法】处理弹窗
# ==========================
def check_alert(self, success_msg):
try:
WebDriverWait(self.driver, 3).until(EC.alert_is_present())
alert = self.driver.switch_to.alert
msg = alert.text
print("弹窗提示:" + msg)
alert.accept()
print(f"✅ {success_msg}")
except:
print("❌ 未出现弹窗,测试失败!")
assert False
# ==========================
# 测试用例(现在超级干净)
# ==========================
# 成功登录的测试用例
def LoginSucTest(self):
self.input_login("zhangsan", "123456")
success_elem = self.driver.find_element(By.CSS_SELECTOR, "body > div.container > div.left > div > h3")
BlogDriver.getScreenshot()
assert success_elem.is_displayed(), "登录失败 未找到首页元素"
print("✅ 博客登录成功测试用例执行通过")
self.driver.back()
# 失败登录:用户名错误
def LoginFail_UserError(self):
self.input_login("zhangsann", "123456")
self.check_alert("用户名错误登录失败弹窗正常出现,测试通过!")
# 失败登录:密码错误
def LoginFail_PassError(self):
self.input_login("zhangsan", "123")
self.check_alert("密码错误登录失败弹窗正常出现,测试通过!")
# 失败登录:用户名+密码错误
def LoginFail_UAPError(self):
self.input_login("zhangggsan", "123")
self.check_alert("用户名 + 密码错误登录失败弹窗正常出现,测试通过!")
# 执行用例
login = BlogLogin()
login.LoginSucTest()
login.LoginFail_UserError()
login.LoginFail_PassError()
login.LoginFail_UAPError()
login.driver.quit()
当有了main之后 其他类下不能有执行代码
博客首页界面:

Python 程序的入口点,作用是:
-
按顺序执行多个测试用例
-
只要把执行代码写在 runTest.py 的
if __name__ == "__main__"里面:不管你在哪个文件里右键点 "运行",只有 runTest.py 里的代码会执行
执行顺序完全按照写的顺序来 ,其他文件绝对不会自己乱运行

python
# 博客登录首页
from selenium.common import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from common1.Utils import BlogDriver
class BlogList:
def __init__(self):
url=""
driver=""
self.url="http://47.108.157.13:8090/blog_list.html"
self.driver=BlogDriver.driver
def ListSucTest(self):
self.driver.implicitly_wait(3)
# 测试昵称是否存在
self.driver.find_element(By.CSS_SELECTOR,"body > div.container > div.left > div > h3")
# 测试头像是否存在
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(2) > div.title")
# 测试博客正文是否存在
self.driver.find_element(By.CSS_SELECTOR,"body > div.container > div.right > div:nth-child(2) > div.desc")
# 测试"查看全文:按钮是否存在
self.driver.find_element(By.CSS_SELECTOR,"body > div.container > div.right > div:nth-child(2) > a")
# 添加屏幕截图
BlogDriver.getScreenshot()
print("登录成功 主页展示完成")
def ListFailTest(self):
# 进入页面
self.driver.get(self.url)
# 等待弹窗出现(未登录拦截的证据!)
try:
WebDriverWait(self.driver, 3).until(EC.alert_is_present())
alert = self.driver.switch_to.alert
assert "用户未登录" in alert.text
print("✅ 未登录访问列表页:弹窗已出现,用例通过!")
alert.accept()
except:
print("❌ 未登录用例失败")
assert False


我们要先验证未登录直接进入的情况 此时RunTest的执行顺序调整为:
python
from common1.Utils import BlogDriver
from tests1.BlogList import BlogList
from tests1.BlogLogin import BlogLogin
if __name__ == "__main__":
# 1. 先执行:未登录访问列表页(失败用例)
print("=== 未登录访问列表页 ===")
BlogList().ListFailTest()
# 2. 执行:登录失败用例
print("=== 登录失败用例 ===")
BlogLogin().LoginFail_UserError()
BlogLogin().LoginFail_PassError()
BlogLogin().LoginFail_UAPError()
# 3. 执行:登录成功 → 列表页成功
print("=== 登录成功 + 列表页成功 ===")
BlogLogin().LoginSucTest()
BlogList().ListSucTest()
# 关闭浏览器
print("=== 所有用例执行完毕 ===")
BlogDriver.driver.quit()
博客详情页:
python
# 博客详情页
from selenium.webdriver.common.by import By
from common1.Utils import BlogDriver
class BlogDetail:
url = ""
driver = ""
def __init__(self):
self.url="http://47.108.157.13:8090/blog_detail.html?blogId=1"
self.driver = BlogDriver.driver
# 登录状态下博客详情页的测试
def DetailSucTest(self):
self.driver.get(self.url)
# 有发布时间
self.driver.find_element(By.CSS_SELECTOR,"body > div.container > div.right > div > div.date")
# 有发布标题
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.operating > button:nth-child(2)")
# 添加屏幕截图
BlogDriver.getScreenshot()
print("详情页展示完成")
博客编辑页面:


python
# 博客编辑页面
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from common1.Utils import BlogDriver
class BlogEdit:
url=""
driver=""
def __init__(self):
self.url="http://47.108.157.13:8090/blog_edit.html"
self.driver=BlogDriver.driver
self.driver.get(self.url)
# 正常发布博客(标题 内容 发布)
def EditSucTest(self):
self.driver.implicitly_wait(3)
self.driver.find_element(By.CSS_SELECTOR, "#title").send_keys("今天是5.26")
# 找到编辑区域,输入关键词(编辑区域不可操作)
# 菜单栏无法元素无法定位
# 本博客系统编辑区域默认情况下就不为空,可以暂不处理
self.driver.find_element(By.CSS_SELECTOR, "#submit").click()
try:
WebDriverWait(self.driver, 3).until(EC.alert_is_present())
alert = self.driver.switch_to.alert
print("成功弹窗提示:", alert.text)
alert.accept()
except:
pass
BlogDriver.getScreenshot()
print("发布博客成功")
# 异常情况(不写标题)
def EditFail_title(self):
self.driver.implicitly_wait(3)
self.driver.find_element(By.CSS_SELECTOR, "#submit").click()
try:
WebDriverWait(self.driver, 3).until(EC.alert_is_present())
alert=self.driver.switch_to.alert
assert "参数校验失败" in alert.text
print("发布博客失败(参数校验失败)")
alert.accept()
except:
assert False, "未弹出校验弹窗"

最后 所有代码的隐式等待可以统一写到Utils:

类函数里 self 到底有什么用





Python self = Java this,只是 Python 要求必须显性写出来。
| 特性 | Python self | Java this |
|---|---|---|
| 含义 | 当前实例对象 | 当前实例对象 |
| 是否要手动声明 | 必须写第一个参数 | 不用写,隐含 |
| 能否改名 | 可以 | 不可以,关键字 |
| 访问成员 | 必须写 self. | 可省略 |
| 能否传参返回 | 可以 | 不可以 |
四.测试报告


=======================================================================
博客系统测试报告




=======================================================================
