目录[-]
目录:
- 引言
- 基础设置
- SeleniumBase使用方法
- 案例和详细解释
- 自定义模块
- pytest-html测试报告
- Allure集成测试报告
——————————————————————————————————————————————————
1.引言
SeleniumBase是github上的一个库,它集成了selenium的主要功能、解决了selenium的痛点、减少了代码量、有更好的库支持、方便进行并行测试和生成测试报告、内置等待和异常处理、增加图片下载功能(用于UI自动化测试)、方便部署、支持多浏览器、多设备等优点,下面详细解释。
- SeleniumBase有--uc属性,模拟人为操作绕 过反爬机制
- SeleniumBase有智能等待机制,不需要显示、隐式等待,也不用写waitforelement相关代码
- SeleniumBase内置失败处理,元素失败时控制台明确报错内容,且失败时自动截图
- SeleniumBase支持多设备,除了web,也支持app
- SeleniumBase截图功能,一行代码完成:self.save_screenshot("debug_3.png")
- SeleniumBase集成pytest和allure,方便生成报告(后续解释)
- SeleniumBase支持手动断点,需下载pdb和trace
——————————————————————————————————————————————————
2.基础设置
seleniumBase对python版本和chromedriver版本是有要求的,可能与本地部分模块存在冲突,最好新建一个虚拟环境,方法如下:
- 新建一个Python目录,如:sb_project,通过cmd或者Pycharm切换到该目录下,输入命令:python -m venv myenv (虚拟环境的名字可自定义,这里取myenv)
- 再输入命令,激活虚拟环境:myenv\Scripts\activate
- 激活后,控制台左侧显示绿色的(env)图标,成功启用虚拟环境
- 删除虚拟环境命令(如果需要删除的话):rm -rf myenv
- 本地Python环境,尽量选新的版本,不是新的也必须是python3对应的版本,这个库不支持py2
——————————————————————————————————————————————————
3.SeleniumBase使用方法
上面建好了虚拟环境,准备好编程语言、编程工具,下面下载SeleniumBase文件:
- 进入GitHub下载,地址(可下载最新版):
-
解压到本地文件夹
-
cmd进入到该目录下,比如图中是,D:\Python\SeleniumBase-4.37.2 ,回到上一级,输入命令:pip install SeleniumBase-4.37.2,如果第一次报错就重新来一次
-
安装后,输入 pip list ,如下右图
————
安装后,编写第一个py文件(这个是官方的示例,不是我编的)
uc=True:使用undetected-chromedriver(避免被网站检测为自动化工具)
from seleniumbase import SB
with SB(uc=True) as sb:
sb.open("https://target.com")
————
4.案例和详细解释
在上面代码的基础上,替换网站为,https://www.baidu.com 下面详细解释元素定位的写法,比如百度网站的输入框,它的前端代码如下:
<input type="text" class="s_ipt" name="wd" id="kw"
maxlength="100" autocomplete="off" placeholder>
- id定位: #id ——》sb.type('#kw','你好'),它有2个参数,右边的参数是针对input输入框,输入的内容
- classname定位:.class ——》sb.type('.s_ipt','你好')
- 属性定位:按照这个逻辑去想,我要找input标签下的name等于wd的标签元素,写法如下:input[name="wd"] ,如果我还有一层div,那就是: div>input[name="wd"] ,如果在div上面还有一层,那就可以一直写,直到根目录为止,只要能保证定位到的是唯一的值,就可以不用再往上级找
- xpath定位:sb.type('//*[@id="kw"]','你好') ,元素定位是最简单的,鼠标右键点击这个元素,自动就给你生成,但是在自动化测试过程中,几乎没人用xpath,因为只要页面元素变了一点点,这个定位就可能不准
- 总结一下,它的元素定位模式与selenium没什么差别,selenium使用classname定位时,经常遇到多个元素的classname值相同的情况,会用到css_selector,SeleniumBase目前没遇到这情况,待补充
————
常见用法,重点加粗:
- sb.open("https://google.com/ncr") ——》打开网站
- sb.save_screenshot("debug_3.png") ——》截图到当前运行程序的目录,文件名是:debug_3.png
- sb.get_page_title() ——》获取当前浏览器title,这个title指的是一个url页面最上方的那个页签title
- sb.click('[href*="github.com/seleniumbase/"]') ——》点击事件,[href*="..."] 是CSS选择器
- sb.type('input[name="wd"]','你好') ——》通过标签名input、再加上属性定位
- sb.scroll_to("div[class='footer_copy']") ——》控制鼠标滑轮滚动
- sb.get_text(selector) ——》获取元素属性对应的文字,比如<a>点击这里</a>
- sb.execute_script('alert("hello,world!"') ——》执行js代码
- sb.upload_file("#file-input", "path/to/file.txt") ——》文件上传下载
- sb.uc_gui_click_capcha(selector) ——》尝试绕过验证码,此时会暂停自动化测试,直到完成验证
- sb.wait_for_element_visible(selector) ——》等待元素出现
- sb.accept_alert() ——》处理弹窗
- sb.hover(selector) ——》鼠标悬停
- sb.drag_and_drop("selector", "selector") ——》鼠标拖拽
- sb.right_click("selector") ——》鼠标右键
- sb.switch_to_frame(selector) ——》切换到 iframe 页
————
常用断言:
- sb.assert_title("Google") ——》断言网页标题
- sb.assert_text('Thank you for your order!') ——》断言页面文字(校验整个页面,不针对某个元素)
- sb.assert_element('progress[value="100"]') ——》断言具体元素的值
- sb.assert_element_present("button.submit") ——》断言元素是否存在
- sb.assert_attribute("#placeholderText", "placeholder", "Placeholder Text Field") ——》3个参数,分别是 selector、attribute、value,校验 元素、属性、值
- sb.assert_true(sb.is_selected("#radioButton2")) ——》断言元素是否选中,用于单选/多选框
- sb.assert_link_text("seleniumbase.com") ——》断言文本链接的内容
————
5.自定义模块
1、自定义模板(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")
2、自定义模块(testcases/test_demo.py):通用断言
# coding=utf-8
from loguru import logger
import pytest
from seleniumbase import BaseCase
from contextlib import contextmanager
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 _log_element_status(self, selector, found=True, value=None):
"""私有方法:记录元素状态"""
status = "✅ 定位成功" if found else "❌ 定位失败"
logger.info(f"{status} - 元素: {selector}" + (f", 值: {value}" if value else ""))
def check(self, selector=None, title=None, url=None, text=None, content_text=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 content_text:
actual_text = self.get_text(selector)
logger.info(f"检查元素文本: [{content_text}]")
assert actual_text == content_text, f"元素文本应为 [{content_text}],实际是 [{actual_text}]"
def check_input(self, selector, expected):
"""带错误收集的输入框断言"""
with self.soft_check():
actual = self.get_value(selector)
self._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")
# 所有检查点会自动收集错误
self.check(title="Web Testing Page")
self.type("#myTextInput", "112233444")
self.check_input("#myTextInput", "112233")
self.type(".textareaClass", "aabbcc-textarea")
self.check_input(".textareaClass", "aabbcc-textarea")
self.check(text="Text...")
self.check("#myButton", content_text="Click Me (Green)")
self.click("#myButton")
self.check("#myButton", content_text="Click Me (Purple)")
self.assert_attribute("#placeholderText", "placeholder", "Placeholder Text Field")
# 最终验证所有错误
self.verify_all()
if __name__ == "__main__":
pytest.main([__file__, "--headless", "--browser=chrome"])
3、终端输入以下命令,进行单元测试:dashboard .html
# 通过 pytest 运行程序,并生成测试报告
pytest main.py --dashboard --rs --headless
4、运行结果
如果有失败的用例,则显示下面内容
————
常见问题
1、如果当前目录有多条待测试文件,怎么一次执行?
答:run_test.py代码里,pytest.main(),可以选择整个文件夹,这样它就会读取该文件夹下,所有的文件,但是有一点需要注意,它依赖于pytest规则,被测试的文件名,需要加上test前缀,如下图
——————————————————————————————————————————————————
6.pytest-html测试报告
run_tests.py,修改pytest.main()里的内容,如下
# 运行测试并生成HTML报告
report_path="html_reports/"+f"{time.strftime('%Y-%m-%d_%H-%M-%S')}"+".html"
pytest.main([
"testcases/",
"--headless",
"--html=" + str(report_path), # 关键参数:生成HTML报告
"--self-contained-html", # 生成单文件HTML(包含CSS/JS)
"-v",
"-s",
"--rs"
])
会在本地根目录下的 html_reports 文件夹里,生成带有时间戳的.html文件
在浏览器打开html文件后,展示内容如下
这是pytest内置的pytest-html库对应的报告样式,不好看但是能用,如果要更好看更契合,就需要Allure的报告
——————————————————————————————————————————————————
7、Allure集成测试报告
# 运行 testcases 下的所有测试,并生成 Allure 报告
pytest.main([
"testcases/test_demo.py",
# "testcases/", # 运行整个目录
"--rs", # 随机测试
"--headless", # 无头测试
"--alluredir=./allure_results", # 通过allue生成报告
"-v", # 显示详细信息,
"-s", # 关键参数:禁用输出捕获
])
运行之前,需要保证本地存在Allure库,下面是安装方法:
- 下载 Allure 源代码(挑选一个版本下载到本地):
- 解压缩,将 bin 目录加入 path 环境变量,如:D:\allure-2.33.0\bin
- 运行单元测试:
pytest main_1.py --rs --headless --alluredir=./allure-results - 查看测试报告:
allure serve ./allure-results - 如果不用命令行,可以通过 run_tests.py 调用程序
# 生成 Allure 报告并自动打开(可选) os.system("allure generate ./allure_results -o ./html_result/ --clean") os.system("allure open -h 127.0.0.1 -p 8083 ./html_result")
实际使用时,遇到的问题总结:
- 定位不到本地待测试文件:pytest在运行时,需要寻找名字叫 test*或_test*等等特定名字的py文件,而这些py文件里面的具体代码,也需要在每个测试用例上,加上test/TEST前缀,这样能精准的查到需运行的测试用例
- 本地想随时随地打开测试报告查看,在根目录,输入:
allure open ./html_result - 但是这样的方法,在输出测试报告时又很繁琐,因为生成的allure_report相关文件,包括index.html、css等目录,如果想只生成一个 index.html 文件,需使用下面的命令
allure generate ./allure_results -o ./html_result --clean --single-file这样执行后,只会生成1个index.html文件,解决了跨域问题,它可以在任何浏览器打开
——————————————————————————————————————————————————
END