目录[-]

搭配Allure库,可实现自动化测试平台,一键生成报告(文件夹格式),不过可以通过后续的Nginx搭建远程服务器,这样就可以实现查看报告

下载地址:https://github.com/seleniumbase/SeleniumBase

————————

1、新建虚拟环境:

python -m venv myenv

2、激活虚拟环境:

myenv\Scripts\activate

3.激活后,只要控制台左侧显示绿色myenv图标,就是成功启用虚拟环境,此时可以用pip安装模块

4.删除虚拟环境:

rm -rf myenv

 

SeleniumBase

  1. 高级封装,简化selenium语法、智能等待
  2. 绕过反爬机制

# coding=utf-8 # 先引用库 from seleniumbase import SB with SB(uc=True) as sb: sb.open("https://target.com")

  1. 丰富的断言

# 断言网页标题 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")

  1. 可视化报告

pytest main.py --dashboard --rs --headless # dashboard:生成实时测试仪表盘 # rs:自动保存测试截图,截图地址是:./lastest_logs/ # headless:无头模式运行,不显示GUI,后台运行 # main.py:指定测试文件

  1. 多浏览器与模式支持

with SB(headless=True, mobile=True,browser="firefox") as sb: sb.open("https://m.example.com")

  1. 内置测试工具链

1.自动下载浏览器驱动

2.集成pytest

3.Page Object模式支持

  1. 扩展的JavaScript控制

# 直接执行JS self.js_click("button:hidden") # 点击隐藏按钮 self.execute_script("alert('Hello')")

  1. 专项解决方案

# 文件上传下载 self.upload_file("#file-input", "path/to/file.txt") # 尝试绕过验证码,实验性功能,暂停自动化脚本直到完成验证 sb.uc_gui_click_captcha()

  1. 性能优化

1.并行测试:通过 pytest-xdist 实现多进程运行

2.缓存复用:浏览器会话可复用,减少启动耗时

  1. 调试友好

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