使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 47--设置Selenium以无头模式运行代码

测试学习记录,仅供参考!

设置Selenium无头模式运行WEB自动化测试代码

Selenium 中比较重要的一个概念,使用无头模式去执行测试化脚本;简单来说就是不会把浏览器给调出来,是在后台运行测试脚本;selenium无头模式是一种运行浏览器操作而无需显示浏览器界面的模式,在无头模式下,浏览器在后台运行,不会弹出窗口或显示用户界面,但仍然能够执行网页的相关操作,如点击、输入、导航等等。

性能提升:由于不需要渲染和显示界面,无头模式可以更高效的执行操作,减少资源消耗;

适用于服务器环境:在服务器上运行自动化任务时,无头模式充许在没有图形界面的环境中执行浏览器操作;

更稳定:由于无头模式不涉及图形界面的显示,可以减少与界面相关的问题,使测试更加稳定;

Selenium 无头浏览器模式

无头浏览器模式(Headless Mode)是指在没有图形用户界面(GUI)的情况下运行浏览器,换句话说,浏览器在后台运行,不会弹出可见的窗口;

无头浏览器模式通常用于自动化测试、网页抓取、性能测试等场景,因为它可以节省系统资源,并且在没有显示器的服务器上也能正常运行;

启用无头模式 :通过 ChromeOptionsFirefoxOptions 添加 --headless 参数;

优化设置:禁用 GPU 加速、设置窗口大小等;

验证无头模式:通过打印页面标题或截图验证脚本运行结果;

为什么使用无头浏览器模式?

节省资源:无头模式不需要渲染图形界面,因此可以节省 CPU 和内存资源;

提高速度:由于不需要加载和渲染图形界面,无头模式通常比普通模式更快;

适合自动化:在自动化测试和网页抓取中,无头模式可以避免干扰,并且可以在没有显示器的服务器上运行;

便于调试:在某些情况下,无头模式可以帮助开发者更快地调试和定位问题;

无头模式下的常见问题及解决方案

页面加载不完全

在无头模式下,有时页面可能加载不完全,导致元素无法找到。可以通过以下方式解决:

增加等待时间 :使用 WebDriverWait 显式等待元素加载完成;

调整窗口大小:有时页面元素的位置和大小会根据窗口大小变化,

可以通过 driver.set_window_size(width, height) 设置窗口大小;

截图和日志

在无头模式下,你可能需要截图或记录日志来调试问题。可以使用以下方法:

截图 :使用 driver.save_screenshot('screenshot.png') 保存当前页面的截图;

日志 :通过 driver.get_log('browser') 获取浏览器日志;

无头浏览器模式是 Selenium 自动化测试和网页抓取中的一个强大工具。通过简单的设置,你可以在没有图形界面的情况下运行浏览器,节省资源并提高效率。无论是 Chrome、Firefox 还是 Edge,Selenium4 都提供了简单的方法来启用无头模式。

以下是如何在无头模式下使用 Selenium 截取百度首页(www.baidu.com)的完整代码示例。

代码中启用了 Chrome 浏览器的无头模式,并截取页面截图保存为文件。

在浏览器初始化操作时去给浏览器设置无头模式属性

1、优化项目根目录 testcase 软件包下 conftest.py 文件;在前置操作初始化浏览器对象时设置无头模式;

复制代码
def init_driver():
    browser_mapping = {
        'Chrome': webdriver.Chrome,
        'Edge': webdriver.Edge,
        'Firefox': webdriver.Firefox
    }
    if browser_type.capitalize() in browser_mapping:
        return browser_mapping.get(browser_type.capitalize())()

2、正常情况下只需在浏览器对象后面传一个参数就行了;但是当前这种方式是采用配置文件方式,在配置文件中设置什么浏览器,再去调用相对应的浏览器对象;

3、简单改造一下;给每一个浏览器加上一个无头模式,通过设置哪个浏览器,就给哪个浏览器加上无头模式;

复制代码
import pytest
from selenium import webdriver
from util_tools.logs_util.recordlog import logs
from config.setting import browser_type, WAIT_TIME
from pageObject.login_page.login_page import LoginPage
import allure
from selenium.webdriver.chrome.options import Options as ChromeOptions
from selenium.webdriver.edge.options import Options as EdgeOptions
from selenium.webdriver.firefox.options import Options as FirefoxOptions

@pytest.fixture(autouse=True)
def log_outputs():
    logs.info('------测试用例开始执行testcase------')
    yield
    logs.info('------测试用例执行完毕testcase------')

def init_driver():
    browser_mapping = {
        'Chrome': webdriver.Chrome,
        'Edge': webdriver.Edge,
        'Firefox': webdriver.Firefox
    }
    if browser_type.capitalize() == 'Chrome':
        options = ChromeOptions()
        options.add_argument('--headless')
    elif browser_type.capitalize() == 'Edge':
        options = EdgeOptions()
        options.add_argument('--headless')
    elif browser_type.capitalize() == 'Firefox':
        options = FirefoxOptions()
        options.add_argument('--headless')
    if browser_type.capitalize() in browser_mapping:
        return browser_mapping.get(browser_type.capitalize())()

@pytest.fixture(scope='class')
def get_driver():
    driver = init_driver()
    driver.implicitly_wait(WAIT_TIME)
    driver.maximize_window()
    yield driver
    driver.quit()

@pytest.fixture(scope='class')
def login_driver(get_driver):
    driver = get_driver
    login_page = LoginPage(driver)
    login_page.login('admin123', '123456')
    return driver

@pytest.fixture()
def not_login_driver():
    global driver
    driver = init_driver()
    driver.implicitly_wait(WAIT_TIME)
    driver.maximize_window()
    yield driver
    driver.quit()

@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport():
    coucome = yield
    result = coucome.get_result()
    if result.when == 'call':
        xfail = hasattr(result, 'wasxfail')
        if (result.skipped and xfail) or (result.failed and not xfail):
            with allure.step('测试用例失败截图'):
                allure.attach(driver.get_screenshot_as_png(), '失败截图', attachment_type=allure.attachment_type.PNG)

设置完成无头模式后使用

4、优化 conftest.py 文件;设置去使用无头模式;

复制代码
import pytest
from selenium import webdriver
from util_tools.logs_util.recordlog import logs
from config.setting import browser_type, WAIT_TIME
from pageObject.login_page.login_page import LoginPage
import allure
from selenium.webdriver.chrome.options import Options as ChromeOptions
from selenium.webdriver.edge.options import Options as EdgeOptions
from selenium.webdriver.firefox.options import Options as FirefoxOptions

@pytest.fixture(autouse=True)
def log_outputs():
    logs.info('------测试用例开始执行testcase------')
    yield
    logs.info('------测试用例执行完毕testcase------')

def init_driver():
    browser_mapping = {
        'Chrome': webdriver.Chrome,
        'Edge': webdriver.Edge,
        'Firefox': webdriver.Firefox
    }
    options = None
    if browser_type.capitalize() == 'Chrome':
        options = ChromeOptions()
        options.add_argument('--headless')
    elif browser_type.capitalize() == 'Edge':
        options = EdgeOptions()
        options.add_argument('--headless')
    elif browser_type.capitalize() == 'Firefox':
        options = FirefoxOptions()
        options.add_argument('--headless')
    if browser_type.capitalize() in browser_mapping:
        return browser_mapping.get(browser_type.capitalize())(options=options)


@pytest.fixture(scope='class')
def get_driver():
    driver = init_driver()
    driver.implicitly_wait(WAIT_TIME)
    driver.maximize_window()
    yield driver
    driver.quit()


@pytest.fixture(scope='class')
def login_driver(get_driver):
    driver = get_driver
    login_page = LoginPage(driver)
    # 自行设置是否参数化
    login_page.login('admin123', '123456')
    return driver



@pytest.fixture()
def not_login_driver():
    global driver
    driver = init_driver()
    driver.implicitly_wait(WAIT_TIME)
    driver.maximize_window()
    yield driver
    driver.quit()


@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport():
    coucome = yield
    result = coucome.get_result()
    if result.when == 'call':
        xfail = hasattr(result, 'wasxfail')
        if (result.skipped and xfail) or (result.failed and not xfail):
            with allure.step('测试用例失败截图'):
                allure.attach(driver.get_screenshot_as_png(), '失败截图', attachment_type=allure.attachment_type.PNG)

5、运行主函数 run.py 文件;会在后台执行测试脚本,在执行过程当中没有调出浏览器来进行操作;

好处是后续部署到服务器上时方便执行,不需要调用浏览器,在后台运行即可;但是只有当 web 自动化测试脚本平时调试比较稳定的情况下才适合使用无头模式,不能一开始就使用无头模式,在一开始就使用无头模式会导致在测试过程中不能更好的去排查问题;

通过配置控制是否以无头模式运行

把无头模式设置到配置文件中;

6、优化项目根目录 configs 软件包下 setting.py 文件;在里面设置一个变量,通过这个变量去控制是否需要有调出浏览器;

复制代码
# 导包
import os
import sys

DIR_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(DIR_PATH)

WAIT_TIME = 10

browser_type = 'edge'
# browser_type = 'chrome'

# 设置浏览器是否以无头模式运行测试脚本,默认为False,则会在测试过程中调出浏览器
# headless = True
headless = False

# 消息是否发送--True是发送,False是不发送
# is_dd_msg = True
is_dd_msg = False

# 自定义机器人签名
secret = 'SEC8bec85af5a1059e5e59b78250704dc53256c09e61501b486c3bf21c21b55b5e5'
# URL地址
webhook = (
    'https://oapi.dingtalk.com/robot/send?access_token=db80eb02dea43ed5ef8e5cfc2cc9f50d747f0ac2c636e9b429e511c362d4ae88')

# 文件路径
FILE_PATH = {
    'log': os.path.join(DIR_PATH, 'log'),
    'screenshot': os.path.join(DIR_PATH, 'screenshot'),
    'ini': os.path.join(DIR_PATH, 'config', 'config.ini')
}

7、优化 conftest.py 文件;引入在配置文件中设置的 headless 变量值;

是否需要调出浏览器是通过 (options=options) 这个值控制的,所以在这里面做个判断就行了;

在 (options=options if headless else None) 增加一个判断(三元运算);

复制代码
import pytest
from selenium import webdriver
from util_tools.logs_util.recordlog import logs
from config.setting import browser_type, WAIT_TIME, headless
from pageObject.login_page.login_page import LoginPage
import allure
from selenium.webdriver.chrome.options import Options as ChromeOptions
from selenium.webdriver.edge.options import Options as EdgeOptions
from selenium.webdriver.firefox.options import Options as FirefoxOptions


@pytest.fixture(autouse=True)
def log_outputs():
    logs.info('------测试用例开始执行testcase------')
    yield
    logs.info('------测试用例执行完毕testcase------')


def init_driver():
    browser_mapping = {
        'Chrome': webdriver.Chrome,
        'Edge': webdriver.Edge,
        'Firefox': webdriver.Firefox
    }
    options = None
    if browser_type.capitalize() == 'Chrome':
        options = ChromeOptions()
        options.add_argument('--headless')
    elif browser_type.capitalize() == 'Edge':
        options = EdgeOptions()
        options.add_argument('--headless')
    elif browser_type.capitalize() == 'Firefox':
        options = FirefoxOptions()
        options.add_argument('--headless')
    if browser_type.capitalize() in browser_mapping:
        return browser_mapping.get(browser_type.capitalize())(options=options if headless else None)


@pytest.fixture(scope='class')
def get_driver():
    driver = init_driver()
    # 设置一个全局的隐式等待时间
    driver.implicitly_wait(WAIT_TIME)
    # 最大化浏览器窗口
    driver.maximize_window()
    yield driver
    driver.quit()


# 登录状态的前置应用
@pytest.fixture(scope='class')
def login_driver(get_driver):
    driver = get_driver
    login_page = LoginPage(driver)
    # 自行设置是否参数化
    login_page.login('admin123', '123456')
    return driver


# 未登录状态的前置应用
@pytest.fixture()
def not_login_driver():
    # 把浏览器初始化对象设置为全局属性driver
    global driver
    driver = init_driver()
    # 设置一个全局的隐式等待时间
    driver.implicitly_wait(WAIT_TIME)
    # 最大化浏览器窗口
    driver.maximize_window()
    yield driver
    driver.quit()


@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport():
    coucome = yield
    result = coucome.get_result()
    if result.when == 'call':
        xfail = hasattr(result, 'wasxfail')
        if (result.skipped and xfail) or (result.failed and not xfail):
            with allure.step('测试用例失败截图'):
                allure.attach(driver.get_screenshot_as_png(), '失败截图', attachment_type=allure.attachment_type.PNG)

未完待续。。。

相关推荐
寻星探路13 小时前
【深度长文】万字攻克网络原理:从 HTTP 报文解构到 HTTPS 终极加密逻辑
java·开发语言·网络·python·http·ai·https
ValhallaCoder16 小时前
hot100-二叉树I
数据结构·python·算法·二叉树
执笔论英雄16 小时前
【大模型学习cuda】入们第一个例子-向量和
学习
wdfk_prog16 小时前
[Linux]学习笔记系列 -- [drivers][input]input
linux·笔记·学习
猫头虎17 小时前
如何排查并解决项目启动时报错Error encountered while processing: java.io.IOException: closed 的问题
java·开发语言·jvm·spring boot·python·开源·maven
八零后琐话17 小时前
干货:程序员必备性能分析工具——Arthas火焰图
开发语言·python
Gary Studio18 小时前
rk芯片驱动编写
linux·学习
mango_mangojuice18 小时前
Linux学习笔记(make/Makefile)1.23
java·linux·前端·笔记·学习