目录[-]

目录:

  1. 引言
  2. 基础设置
  3. SeleniumBase使用方法
  4. 案例和详细解释
  5. 自定义模块
  6. pytest-html测试报告
  7. 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版本是有要求的,可能与本地部分模块存在冲突,最好新建一个虚拟环境,方法如下:

  1. 新建一个Python目录,如:sb_project,通过cmd或者Pycharm切换到该目录下,输入命令:python -m venv myenv     (虚拟环境的名字可自定义,这里取myenv)
  2. 再输入命令,激活虚拟环境:myenv\Scripts\activate
  3. 激活后,控制台左侧显示绿色的(env)图标,成功启用虚拟环境
  4. 删除虚拟环境命令(如果需要删除的话):rm -rf myenv
  5. 本地Python环境,尽量选新的版本,不是新的也必须是python3对应的版本,这个库不支持py2

——————————————————————————————————————————————————

3.SeleniumBase使用方法

 

  上面建好了虚拟环境,准备好编程语言、编程工具,下面下载SeleniumBase文件:

  1. 进入GitHub下载,地址(可下载最新版):

    https://github.com/seleniumbase/SeleniumBase/tree/v4.37.2

  2. 解压到本地文件夹

  3. cmd进入到该目录下,比如图中是,D:\Python\SeleniumBase-4.37.2  ,回到上一级,输入命令:pip install SeleniumBase-4.37.2,如果第一次报错就重新来一次

  4. 安装后,输入 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>
  1. id定位: #id    ——》sb.type('#kw','你好'),它有2个参数,右边的参数是针对input输入框,输入的内容
  2. classname定位:.class  ——》sb.type('.s_ipt','你好')
  3. 属性定位:按照这个逻辑去想,我要找input标签下的name等于wd的标签元素,写法如下:input[name="wd"]   ,如果我还有一层div,那就是:  div>input[name="wd"] ,如果在div上面还有一层,那就可以一直写,直到根目录为止,只要能保证定位到的是唯一的值,就可以不用再往上级找
  4. xpath定位:sb.type('//*[@id="kw"]','你好')    ,元素定位是最简单的,鼠标右键点击这个元素,自动就给你生成,但是在自动化测试过程中,几乎没人用xpath,因为只要页面元素变了一点点,这个定位就可能不准
  5. 总结一下,它的元素定位模式与selenium没什么差别,selenium使用classname定位时,经常遇到多个元素的classname值相同的情况,会用到css_selector,SeleniumBase目前没遇到这情况,待补充

————

常见用法,重点加粗:

  1. sb.open("https://google.com/ncr")    ——》打开网站
  2. sb.save_screenshot("debug_3.png")  ——》截图到当前运行程序的目录,文件名是:debug_3.png
  3. sb.get_page_title() ——》获取当前浏览器title,这个title指的是一个url页面最上方的那个页签title
  4. sb.click('[href*="github.com/seleniumbase/"]')  ——》点击事件,[href*="..."] 是CSS选择器
  5. sb.type('input[name="wd"]','你好')  ——》通过标签名input、再加上属性定位
  6. sb.scroll_to("div[class='footer_copy']")  ——》控制鼠标滑轮滚动
  7. sb.get_text(selector)   ——》获取元素属性对应的文字,比如<a>点击这里</a>
  8. sb.execute_script('alert("hello,world!"')  ——》执行js代码
  9. sb.upload_file("#file-input", "path/to/file.txt")  ——》文件上传下载
  10. sb.uc_gui_click_capcha(selector)  ——》尝试绕过验证码,此时会暂停自动化测试,直到完成验证
  11. sb.wait_for_element_visible(selector)  ——》等待元素出现
  12. sb.accept_alert()  ——》处理弹窗
  13. sb.hover(selector)  ——》鼠标悬停
  14. sb.drag_and_drop("selector", "selector")  ——》鼠标拖拽
  15. sb.right_click("selector")  ——》鼠标右键
  16. sb.switch_to_frame(selector)  ——》切换到 iframe 页

————

常用断言:

  1. sb.assert_title("Google")  ——》断言网页标题
  2. sb.assert_text('Thank you for your order!')  ——》断言页面文字(校验整个页面,不针对某个元素)
  3. sb.assert_element('progress[value="100"]')  ——》断言具体元素的值
  4. sb.assert_element_present("button.submit")  ——》断言元素是否存在
  5. sb.assert_attribute("#placeholderText", "placeholder", "Placeholder Text Field")  ——》3个参数,分别是 selector、attribute、value,校验 元素属性
  6. sb.assert_true(sb.is_selected("#radioButton2"))  ——》断言元素是否选中,用于单选/多选框
  7. 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库,下面是安装方法:

  1. 下载 Allure 源代码(挑选一个版本下载到本地):

    https://github.com/allure-framework/allure2/releases

  2. 解压缩,将 bin 目录加入 path 环境变量,如:D:\allure-2.33.0\bin
  3. 运行单元测试:
    pytest main_1.py --rs --headless --alluredir=./allure-results
  4. 查看测试报告:
    allure serve ./allure-results
  5. 如果不用命令行,可以通过 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")
    

实际使用时,遇到的问题总结:

  1. 定位不到本地待测试文件:pytest在运行时,需要寻找名字叫 test*或_test*等等特定名字的py文件,而这些py文件里面的具体代码,也需要在每个测试用例上,加上test/TEST前缀,这样能精准的查到需运行的测试用例
  2. 本地想随时随地打开测试报告查看,在根目录,输入:
    allure open ./html_result
    
  3. 但是这样的方法,在输出测试报告时又很繁琐,因为生成的allure_report相关文件,包括index.html、css等目录,如果想只生成一个 index.html 文件,需使用下面的命令
    allure generate ./allure_results -o ./html_result --clean --single-file
    

    这样执行后,只会生成1个index.html文件,解决了跨域问题,它可以在任何浏览器打开

——————————————————————————————————————————————————

END