目录[-]
搭配Allure库,可实现自动化测试平台,一键生成报告(文件夹格式),不过可以通过后续的Nginx搭建远程服务器,这样就可以实现查看报告
下载地址:https://github.com/seleniumbase/SeleniumBase
————————
1、新建虚拟环境:
python -m venv myenv
2、激活虚拟环境:
myenv\Scripts\activate
3.激活后,只要控制台左侧显示绿色myenv图标,就是成功启用虚拟环境,此时可以用pip安装模块
4.删除虚拟环境:
rm -rf myenv
SeleniumBase
- 高级封装,简化selenium语法、智能等待
- 绕过反爬机制
# coding=utf-8 # 先引用库 from seleniumbase import SB with SB(uc=True) as sb: sb.open("https://target.com")
- 丰富的断言
# 断言网页标题 self.assert_title("Google") # 断言元素是否存在 self.assert_element_present("button.submit") # 断言标签内文本内容 self.assert_exact_text("Login Success", "div.message") # 断言普通元素如div,检查innerText;输入框input,检查value self.assert_text("#myInput","111222")
- 可视化报告
pytest main.py --dashboard --rs --headless # dashboard:生成实时测试仪表盘 # rs:自动保存测试截图,截图地址是:./lastest_logs/ # headless:无头模式运行,不显示GUI,后台运行 # main.py:指定测试文件
- 多浏览器与模式支持
with SB(headless=True, mobile=True,browser="firefox") as sb: sb.open("https://m.example.com")
- 内置测试工具链
1.自动下载浏览器驱动
2.集成pytest
3.Page Object模式支持
- 扩展的JavaScript控制
# 直接执行JS self.js_click("button:hidden") # 点击隐藏按钮 self.execute_script("alert('Hello')")
- 专项解决方案
# 文件上传下载 self.upload_file("#file-input", "path/to/file.txt") # 尝试绕过验证码,实验性功能,暂停自动化脚本直到完成验证 sb.uc_gui_click_captcha()
- 性能优化
1.并行测试:通过 pytest-xdist 实现多进程运行
2.缓存复用:浏览器会话可复用,减少启动耗时
- 调试友好
1.失败自动保存截图和HTML文件
2.交互式调试:self.pause() # 暂停测试,进入交互式命令行
使用方法
1.下载方式,进入 github 仓库下载 zip 到本地
https://github.com/seleniumbase/SeleniumBase/tree/v4.37.2
2.解压缩后,得到文件夹
3.控制台打开该页面,输入 pip install 安装
(如果第一次报错就重新来一次,之前报错过pdbp没有安装上)
4.安装后,输入 pip list ,查看列表页是否存在库名:
也可以在 python 库里,查看文件
第一个案例(干净的目录)
# coding=utf-8
from seleniumbase import SB
'''
使用 with 语句创建 SB 实例,这能确保浏览器会话正确关闭
test=True:启用测试模式(自动记录日志和截图)
uc=True:使用undetected-chromedriver(避免被网站检测为自动化工具)
'''
with SB(test=True, uc=True) as sb:
# ncr表示no country redirect(禁止国家重定向)确保访问国际版Google
sb.open("https://google.com/ncr")
# 通过css选择器,定位并输入文本并回车,\n代表回车
sb.type('[title="Search"]', "SeleniumBase GitHub page\n")
# 点击包含特定href属性的链接,【href*="..."】是CSS选择器
sb.click('[href*="github.com/seleniumbase/"]')
# 截取当前页面截图并保存到日志目录(默认在 ./latest_logs/ 文件夹)
sb.save_screenshot_to_logs()
# 获取当前页面的标题并打印到控制台
print(sb.get_page_title())
用法1:通过 id 取值,如现在有百度输入框的一行信息
<input type="text" class="s_ipt" name="wd" id="kw"
maxlength="100" autocomplete="off" placeholder>
选择器 示例 说明
#id "#kw" 按 ID 定位
.class ".submit-button" 按 class 定位
tag[attr=value] "input[name='email']" 按属性定位
:contains(text) "a:contains('Sign in')" 匹配包含指定文本的元素(扩展)
————————
sb.type('#wd','你好') # 通过ID定位
sb.type('.s_ipt','你好') # 通过class(name)定位
sb.type('input[name="wd"]','你好') # 通过标签名input、再加上属性定位
sb.type('//*[@id="kw"]','你好') # 通过xpath定位
————————
# 示例2:跳过人机验证
# uc=True(使用隐身模式)
with SB(uc=True, test=True, locale="en",browser="chrome") as sb:
url = "https://gitlab.com/users/sign_in"
# 激活 Chrome DevTools Protocol模式,允许通过底层协议控制浏览器,和uc使用
sb.activate_cdp_mode(url)
# 尝试绕过验证码,实验性功能,暂停自动化脚本直到完成验证
sb.uc_gui_click_captcha()
————————
# 示例3:电商平台,定位元素、加入购物车等
from seleniumbase import BaseCase
BaseCase.main(__name__, __file__) # Call pytest
class MyTestClass(BaseCase):
def test_swag_labs(self):
self.open("https://www.saucedemo.com")
# 截图
self.save_screenshot_to_logs(name="main.png")
# 账号密码输入,密码输入带一个\n回车,等同于提交按钮
self.type("input[name='user-name']","standard_user")
self.type("input[name='password']", "secret_sauce\n")
# 鼠标滚轮滑动
self.scroll_to("div[class='footer_copy']")
# 校验页面文字
self.assert_text('Thank you for your order!')
————————
# 安装2个库,pdb和trace,终端命令:trace可以让代码在手动控制下跳转
# 输入字母n和回车,执行一行;输入c,继续执行直到结束或下一个断点,退出按q
import pdb
pdb.set_trace() # 手动断点
pytest test_fail.py --pdb
pytest test_coffee_cart.py --trace
————————
<label id="progressLabel" for="progressBar">Progress Bar: (40%)</label>
原理:根据左侧左右滑动按钮来控制右侧value字段的值
# 校验右侧进度条,数值50
self.assert_element('progress[value="50"]')
# 调整左侧滑轮到100
self.set_value("input#mySlider", "100")
# 校验右侧进度条,数值100
self.assert_element('progress[value="100"]')
————————
<input type="radio" checked="" id="radioButton1" name="radioGroup1"
class="radioClass1" style="width: 30px;">
<input type="radio" id="radioButton2" name="radioGroup1"
class="radioClass1" style="width: 30px;">
click 切换已选内容
# 校验是否选中button 1
assert self.is_selected("#radioButton1")
# 点击另一个单选选项
self.click(selector="#radioButton2")
# 校验是否选中button 2
assert self.is_selected("#radioButton2")
多选框,checkbox,先遍历所有元素,再手动调整需要用到哪些元素
# 14.左8 CheckBoxes 3个多选框
checkboxes=self.find_elements(selector="input[type='checkbox']")
# print(checkboxes)
for index,checkbox in enumerate(checkboxes,1):
checkbox_id=checkbox.get_attribute("id")
if 1<index<5:
self.click(selector="#{}".format(checkbox_id))
————————
<a id="myLink1" name="linkName1" class="linkClass" href="https://seleniumbase.com">
seleniumbase.com</a>
<a id="myLink2" name="linkName2" class="linkClass" href="https://github.com/seleniumbase/SeleniumBase">
SeleniumBase on GitHub</a>
# 校验2个链接的文字显示
self.assert_link_text("seleniumbase.com")
self.assert_link_text("SeleniumBase on GitHub")
# 这个比较特别,使用后一闪一闪的
self.highlight("h2")
下拉框
通过id定位,直接取下面<option>标签里的value值作为第2个参数即可
self.select_option_by_value("#mySelect","50%")
跳转iframe
# 跳转到iframe页
self.switch_to_frame("#myFrame2")
# 检查iframe里的元素内容
self.check(selector="html>body>h4",value="iFrame Text")
# 切换回到默认页
self.switch_to_default_content()
————————
自定义模板(run_tests.py):集成pytest、allure
# run_tests.py
import pytest
from loguru import logger
if __name__ == "__main__":
# 1. 配置日志(自动创建logs目录,30天过期)
logger.add(
"logs/" + "{time:YYYY-MM-DD HH-mm-ss}" + ".log", # 日志文件路径
rotation="00:00", # 每天新建一个日志文件
retention=30, # 保留30天日志
level="INFO", # 记录INFO及以上级别
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}"
)
# 运行 testcases 下的所有测试,并生成 Allure 报告
pytest.main([
"testcases/test_demo.py",
# "testcases/", # 运行整个目录
"--rs", # 随机测试
"--headless", # 无头测试
"--alluredir=./allure_results", # 通过allue生成报告
"-v", # 显示详细信息,
"-s", # 关键参数:禁用输出捕获
])
# 如果想只运行 main_1.py 和 main_2.py,改成:
# pytest.main(["testcases/main_1.py", "testcases/main_2.py", "--alluredir=./allure-results"])
# 生成 Allure 报告并自动打开(可选)
# os.system("allure generate ./allure_results -o ./html_result/ --clean")
#
# os.system("allure open -h 127.0.0.1 -p 8083 ./html_result")
自定义模块(testcases/test_demo.py):通用断言
# coding=utf-8
from loguru import logger
import pytest
from seleniumbase import BaseCase
from contextlib import contextmanager
def _log_element_status(selector, found=True, value=None):
"""私有方法:记录元素状态"""
status = "✅ 定位成功" if found else "❌ 定位失败"
logger.info(f"{status} - 元素: {selector}" + (f", 值: {value}" if value else ""))
class SmartBase(BaseCase):
"""增强版断言基类(带错误收集功能)"""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._errors = [] # 用于收集所有错误信息
@contextmanager
def soft_check(self):
"""软断言上下文管理器"""
try:
yield
except AssertionError as e:
logger.error(f"❌ 检查失败但继续执行: {str(e)}")
self._errors.append(str(e))
self.save_screenshot(f"error_{len(self._errors)}.png") # 自动截图
def verify_all(self):
"""验证所有收集的错误"""
if self._errors:
error_msg = "\n".join([f"{i + 1}. {e}" for i, e in enumerate(self._errors)])
raise AssertionError(f"测试完成,共发现 {len(self._errors)} 处错误:\n{error_msg}")
def check(self, selector=None, title=None, url=None, text=None, value=None):
"""万能断言方法(带错误收集)"""
with self.soft_check(): # 包裹每个检查点
if title:
current_title = self.get_title()
logger.info(f"检查标题: 预期=[{title}], 实际=[{current_title}]")
assert current_title == title, f"标题应为 [{title}],实际是 [{current_title}]"
if url:
current_url = self.get_current_url()
logger.info(f"检查URL: 预期=[{url}], 实际=[{current_url}]")
assert current_url == url, f"URL 应为 [{url}],实际是 [{current_url}]"
if text:
logger.info(f"检查页面是否包含文本: [{text}]")
assert text in self.get_page_source(), f"页面应包含 [{text}]"
if value:
actual_text = self.get_text(selector)
logger.info(f"检查元素文本: [{value}]")
assert actual_text == value, f"元素文本应为 [{value}],实际是 [{actual_text}]"
def check_input(self, selector, expected):
"""带错误收集的输入框断言"""
with self.soft_check():
actual = self.get_value(selector)
_log_element_status(selector, value=actual)
assert actual == expected, f"输入框内容应为 [{expected}],实际是 [{actual}]"
class TestDemo(SmartBase):
def test_example(self):
# 打开浏览器
self.open("https://seleniumbase.io/demo_page")
# 所有检查点会自动收集错误
# 1、监察网页title
self.check(title="Web Testing Page")
# 2.左1 input输入框
self.type("#myTextInput", "112233")
self.check_input("#myTextInput", "112233")
# 3.右1 textarea
self.type(".textareaClass", "aabbcc-textarea")
self.check_input(".textareaClass", "aabbcc-textarea")
# 4.左2 input输入框,默认文字
self.check(text="Text...")
# 5.右2 默认green,点击后变成purple,并且会影响下面的2个字段
self.check("#myButton", value="Click Me (Green)")
self.click("#myButton")
self.check("#myButton", value="Click Me (Purple)")
# 6.左3 input输入框,placeholder文字
self.assert_attribute("#placeholderText", "placeholder", "Placeholder Text Field")
# 7.右3 input只读输入框,value值,受到上面影响,默认是 Green ,但是点击了上面按钮后,这个值就是Purple
self.check(selector="#readOnlyText",value="The Color is Purple")
# 8.左5 进度条 默认value值=50,修改值后重新校验
self.check(selector="#mySlider",value="50")
self.set_value("#mySlider","80")
self.check(selector="#mySlider",value="80")
# 9.右5 不可拖拽进度条,来源于左5的数据,上面拖拽到80,那么它的默认值就是80
self.check(selector="#progressLabel",value="Progress Bar: (80%)")
# 10.左6 下拉框,4个选项,Set to 25% 这里直接调用选择
self.select_option_by_value("#mySelect","50%")
# 11.右6 不可拖拽进度条,来源于左6的数据
self.check(selector="#meterLabel",value="HTML Meter: (50%)")
# 12.左7 iframe里的图片,再切换回来
self.switch_to_frame("#myFrame2")
self.check(selector="html>body>h4",value="iFrame Text")
self.switch_to_default_content()
# 13.右7 radiobutton单选框,先校验默认选中项,再切换到另一项,再次校验
assert self.is_selected("#radioButton1")
self.click(selector="#radioButton2")
assert self.is_selected("#radioButton2")
# 14.左8 CheckBoxes 3个多选框
checkboxes=self.find_elements(selector="input[type='checkbox']")
# print(checkboxes)
for index,checkbox in enumerate(checkboxes,1):
checkbox_id=checkbox.get_attribute("id")
if 1<index<5:
self.click(selector="#{}".format(checkbox_id))
# 15.左9 URL Link
self.check(selector="#myLink1",value="seleniumbase.com")
# 16.上方 Hower Dropdown,鼠标停留,然后点击其中1项
self.hover(selector="#myDropdown")
self.click(selector="#dropOption1")
# 17.如果上面这个点击了,下面这个校验就能通过
self.check(text="Link One Selected")
# 最终验证所有错误
# self.verify_all()
if __name__ == "__main__":
pytest.main([__file__, "--headless", "--browser=chrome"])
————————
终端输入以下命令,进行单元测试:dashboard .html
# 通过 pytest 运行程序,并生成测试报告 pytest main.py --dashboard --rs --headless
问题1:如果当前目录有多条待测试文件,怎么一次执行?
执行后,生成2个文件夹和1个.html测试报告文件,可直接点击 .html 查看测试报告
Allure集成(生成可视化报告)
1.下载allure文件
地址:https://github.com/allure-framework/allure2/releases
下载后解压缩,将bin目录加入到path环境变量,如:D:\allure-2.33.0\bin
————————
2.安装依赖库
# 安装依赖库()
pip install seleniumbase pytest allure-pytest
————————
3.运行单元测试
# 运行测试并保存结果(无头模式) pytest main_1.py --rs --headless --alluredir=./allure-results
————————
4.查看测试报告
# 开启一个本地服务读取本地文件() allure serve ./allure-results
————————
5.pycharm中运行程序
在根目录创建一个run_test.py文件,内容如下,可用于控制执行单个.py文件,还是执行整个文件夹下的文件
# run_tests.py
import pytest
import os
if __name__ == "__main__":
# 运行 testcases 下的所有测试,并生成 Allure 报告
pytest.main([
"testcases/test_main_1.py",
# "testcases/", # 运行整个目录
"--rs", # 随机测试
"--headless", # 无头测试
"--alluredir=./allure_results", # 通过allue生成报告
"-v" # 显示详细信息,
])
# 如果想只运行 main_1.py 和 main_2.py,改成:
# pytest.main(["testcases/main_1.py", "testcases/main_2.py", "--alluredir=./allure_results"])
# 清空上一次的目录文件,保持环境纯净
os.system("allure generate ./allure_results -o ./html_result/ --clean")
# 本地启动服务器,查看 Allure 报告
os.system("allure open -h 127.0.0.1 -p 8083 ./html_result")
实际使用时遇到的问
1.testcases定位整个目录,但是一个.py测试用例都没有添加进去,这是因为pytest运行时,需要寻找名字叫做test*或者_test*之类的特定名字的py,而且当找到这些名字后,类名也最好带上test/TEST,这样才能精准匹配到
# 将测试结果打包()
allure generate ./allure-results -o ./allure-report --clean
# 本地预览(用于验证测试报告的正确性)
allure open ./allure-report
2.生成的文件目录,点击index.html后,打开的网页报错CORS错误,这就需要在本地启一个python服务器
# 进入报告目录 cd allure-report # 启动服务器(默认端口 8000) python -m http.server 8000
3.如果是生产环境,需要Nginx并配置环境,在你本地启一个服务器,用于访问http://localhost:8080
server { listen 8080; server_name localhost; location / { root /path/to/allure-report; index index.html; } }
4.如果只生成一个index.html文件,将allure所有的内容浓缩在一个文件里,则需要命令
allure generate ./allure_results -o ./html_result --clean --single-file