问题贴:__init__初始化方法调用conftest.py文件中的全局变量web_env,不等web_env经过pytest_configure方法就被调用了

问题

问题贴:实例化driver的代码放到基类BasePage中的__init__初始化方法了,但是当__init__方法调用conftest.py文件中的全局变量web_env(用于存放经过多浏览器运行命令后的浏览器的类型名称),不等web_env经过pytest_configure方法就被调用了

想要实现的效果:
能让web_env这个变量能正常被__init__方法调用

报错信息

环境

python 3

你上代码看看,我怀疑你是没完全放到init里,或者你提前在某个地方提前init了。

代码结构:
image

conftest.py:

# 创建conftest.py 文件 ,将下面内容添加进去,运行脚本
# 在conftest.py配置里写方法可以实现数据共享,不需要import导入,可以跨文件共享
from _pytest.config import Config
from _pytest.config.argparsing import Parser


# 重写hook函数,修改默认编码unicode格式的测试用例,改为支持中文格式的unicode-escape
def pytest_collection_modifyitems(items):
    """
    测试用例收集完成时,将收集到的用例名name和用例标识nodeid的中文信息显示在控制台上
    """
    for i in items:
        i.name = i.name.encode("utf-8").decode("unicode_escape")
        i._nodeid = i.nodeid.encode("utf-8").decode("unicode_escape")


# 定义全局变量,用于存储浏览器的类型名称
web_env = {}


# 实现命令行注册,解决自定义参数(设置多浏览器运行命令行参数)报错问题
def pytest_addoption(parser: Parser):
    """
    hook函数
    1、通过 parser.getgroup创建/获取组名: hogwarts
    2、addoption添加一个命令行选项:pytest xxx.py --browser=chrome
    :param parser:
    :return:
    """
    hogwarts = parser.getgroup("hogwarts")
    # pytest .\test_demo.py --browser=chrome
    # pytest .\test_demo.py --driver=chrome
    # 注册一个命令行参数
    hogwarts.addoption("--browser", default="chrome", help="指定执行的浏览器")
    # 第一个参数"--browser"为指定的命令行参数的形式:--browser=chrome、--driver=chrome
    # 第二个参数 default="firefox" 指定参数的默认值
    # 第三个参数 dest = "browser" 用于改写 第一个参数 "--browser" 的名称
    # 第四个参数 help="指定执行的浏览器" 用于给 hogwarts 组名 做解释


def pytest_configure(config: Config):
    """
    hook函数: 用于获取到指定传参参数的值(这个值为浏览器的名称,例如chrome)
    :param config:
    :return:
    """
    config.addinivalue_line("markers",
                            "test_normal: mark test functions that test data type errors")
    config.addinivalue_line("markers",
                            "test_data_type_error: mark test functions that test data type errors")
    config.addinivalue_line("markers",
                            "test_data_range: mark test functions that test data type errors")
    config.addinivalue_line("markers",
                            "test_zero_division_error: mark test functions that test data type errors")
    browser = config.getoption("--browser")
    browser_conf = {"browser": browser}
    print(f"通过命令行获取到的浏览器名称为{browser_conf}")
    web_env.update(browser_conf)
    print(web_env)

base_page.py:

""" 基本类 """
import time
import os
from time import sleep

import allure
import yaml
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait

from conftest import web_env


# @auto_save_exception_screen_shot

class BasePage:
    """定义基地址"""
    _BASE_URL = "https://work.weixin.qq.com/wework_admin/frame#index"

    def __init__(self, base_driver=None):
        """
        初始化
        1、使用命令行参数、兼容多种类型的浏览器运行程序
        2、实例化 self.driver,并复用浏览器
        3、为每个页面设置 3 秒隐式等待
        4、最大化浏览器窗口
        5 、访问基地址 (访问企业微信登录页)
        :param base_driver: 实例化类时,自带的实例化过的driver对象
        """
        self.browser = web_env["browser"]
        print(self.browser)
        print(f"web_env的值为:{self.browser}")
        if base_driver:
            self.driver = base_driver
        else:
            print(web_env["browser"])
            if self.browser == "chrome":
                # 实例化Firefox浏览器的 self.driver,并复用浏览器
                self.driver = webdriver.Chrome()
            else:
                # 实例化Firefox浏览器的 self.driver,并复用浏览器
                self.driver = webdriver.Firefox()
            self.reuse_browser()
            self.driver.implicitly_wait(5)
            self.driver.maximize_window()

        if not self.driver.current_url.startswith("http"):
            # 如果当前访问的浏览器url地址不是http网页,那么将导航到基地址中
            self.driver.get(self._BASE_URL)
        # 设置扫二维码登录的等待时间
        sleep(10)

    def reuse_browser(self):
        """
        复用浏览器
        1、实例化Options对象
        2、修改 option 的 debugger_address 属性:"localhost:9222"
        3、实例化 driver,需添加 Chrome 的 option 配置
        :return:
        """
        # 实例化Options对象
        option = Options()
        # 修改 option 的 debugger_address 属性:"localhost:9222", 这样可以使 localhost:9222 在 debug 模式下打开,便于调试
        option.debugger_address = "localhost:9222"
        # 实例化 driver,需添加 Chrome 的 option 配置
        self.driver = webdriver.Chrome(options=option)

    def get_cookies(self):
        """
        复用cookie
        1、登录成功后,获取 cookie 信息
        2、将 cookie 存入本地文件
        :return:
        """
        # 登录成功后,获取 cookie 信息
        cookies = self.driver.get_cookies()  # 返回一个装了多个 cookie 的列表
        # 将 cookie 存入本地文件
        with open("../data/cookies.yaml", "w", encoding="utf8") as f:
            # yaml.safe_dump , 将目标数据存放到指定文件
            yaml.safe_dump(cookies, f)

    def add_cookies(self):
        """
        植入 cookie
        1、从本地获取 cookies
        2、为 self.driver 实例植入 cookie
        3、退出
        4、复用 cookie 登录
        :return:
        """
        # 获取本地 cookies
        with open("../data/cookies.yaml", "r", encoding="utf8") as f:
            cookies = yaml.safe_load(open("../data/cookies.yaml"))
        # 植入 cookie
        for cookie in cookies:
            self.driver.add_cookie(cookie)
            print(cookie)

    def get_screen_shot(self):
        """
        截图
        1、设置截图保存路径,用带有时间戳的方式命名截图
        2、截图, 并将截图保存到本地文件中
        3、将截图导入到 allure 报告中
        :return:
        """
        time_tamp = time.time()
        image_path = f"../test_case/image_path/image_{time_tamp}.png"
        self.driver.save_screenshot(image_path)
        allure.attach.file(image_path, name="picture", attachment_type=allure.attachment_type.PNG)

    def get_page_source(self):
        """
        获取page_source
        1、设置page_source本地保存路径,用带有时间戳的方式命名page_source
        2、访问到当前页面,并将 page_source 保存到本地文件中
        3、截图
        4、将 page_source 导入到 allure 报告中
        :return:
        """
        time_tamp = time.time()
        page_source_path = f"../test_case/page_source/page_source_{time_tamp}.html"
        ele = self.driver.current_url
        self.driver.get(ele)
        with open(page_source_path, "w", encoding="utf8") as f:
            f.write(self.driver.page_source)
        allure.attach.file(page_source_path, name="page_source", attachment_type=allure.attachment_type.HTML)
        self.get_screen_shot()

    def do_find(self, by, locator=None):
        """
        定位界面某个元素节点
        1、如果传入的是元素节点,则直接传参
        2、如果传入的是带定位方式的元组,则先解包后传参
        :param by:定位方式, 例:By.ID、By.NAME等
        :param locator: 元素节点
        :return: 返回定位到的元素节点对象
        """
        if locator:
            return self.driver.find_element(by, locator)
        else:
            return self.driver.find_element(*by)

    def do_finds(self, by, locator=None):
        """
        定位界面多个元素节点
        1、如果传入的是元素节点,则直接传参
        2、如果传入的是带定位方式的元组,则先解包后传参
        :param by:定位方式, 例:By.ID、By.NAME等
        :param locator: 元素节点
        :return: 返回定位到的元素节点对象列表
        """
        if locator:
            return self.driver.find_elements(by, locator)
        else:
            return self.driver.find_elements(*by)

    def do_send_keys(self, value, by, locator=None):
        """
        输入框中输入信息
        1、定位到输入框的位置
        2、调用sendkeys()方法,输入信息
        :return:
        """
        ele = self.do_find(by, locator)
        ele.clear()
        ele.send_keys(value)

    def webdriver_wait_until_element_find(self, locator: tuple):
        """
        封装显示等待的等待条件:直到元素被找到,才能进行下一步操作
        :param locator: (定位方式,元素节点)
        :return: 返回等待条件结果为真
        """
        return WebDriverWait(self.driver, 10).until(expected_conditions.visibility_of_element_located(locator))

    def do_quit(self):
        """
        退出浏览器,销毁浏览器进程
        :return:
        """
        self.driver.quit()

你的报错信息贴的不全,你把完整的报错堆栈发出来。我看了你的代码web_env一开始就初始化了。

 10.0.26100.2161]
(c) Microsoft Corporation。保留所有权利。

D:\开发项目\selenium_test_WeCom>cd test_case

D:\开发项目\selenium_test_WeCom\test_case>pytest test_wecom.py --browser=chrome
通过命令行获取到的浏览器名称为{‘browser’: ‘chrome’}
{‘browser’: ‘chrome’}
======================================== test session starts =========================================
platform win32 – Python 3.12.0, pytest-8.3.3, pluggy-1.5.0 – D:\python3\python.exe
cachedir: .pytest_cache
rootdir: D:\开发项目\selenium_test_WeCom
configfile: pytest.ini
plugins: allure-pytest-2.13.5, ordering-0.6, xdist-3.6.1
collecting … [[‘Tony’, ‘test_01’, ‘12345678899’, ‘1585736247@qq.com’]]
collected 1 item

test_wecom.py::TestWeCom::test_add_member[Tony-test_01-12345678899-1585736247@qq.com] ERROR

=============================================== ERRORS ===============================================
______ ERROR at setup of TestWeCom.test_add_member[Tony-test_01-12345678899-1585736247@qq.com] _______

self = <class ‘selenium_test_WeCom.test_case.test_wecom.TestWeCom’>

def setup_class(self):
    """
    复用 cookie 登录
    1、登录成功后,获取 cookies 信息
    2、将 cookies 存入本地文件
    3、从本地获取 cookies
    4、为 self.driver 实例植入 cookie
    5、退出
    6、复用 cookie 登录
    :return:
    """
  self.home = LoginPage().reuse_cookies_login()

test_wecom.py:30:


self = <page_obj.login_page.LoginPage object at 0x0000023D79441610>, base_driver = None

def __init__(self, base_driver=None):
    """
    初始化
    1、使用命令行参数、兼容多种类型的浏览器运行程序
    2、实例化 self.driver,并复用浏览器
    3、为每个页面设置 3 秒隐式等待
    4、最大化浏览器窗口
    5 、访问基地址 (访问企业微信登录页)
    :param base_driver: 实例化类时,自带的实例化过的driver对象
    """
    if base_driver:
        self.driver = base_driver
    else:
      self.browser = web_env["browser"]

E KeyError: ‘browser’

…\page_obj\base_page.py:45: KeyError
====================================== short test summary info =======================================
ERROR test_wecom.py::TestWeCom::test_add_member[Tony-test_01-12345678899-1585736247@qq.com] - KeyError: ‘browser’
========================================== 1 error in 0.16s ==========================================

D:\开发项目\selenium_test_WeCom\test_case>pytest test_wecom.py --browser=chrome
通过命令行获取到的浏览器名称为{‘browser’: ‘chrome’}
{‘browser’: ‘chrome’}
======================================== test session starts =========================================
platform win32 – Python 3.12.0, pytest-8.3.3, pluggy-1.5.0 – D:\python3\python.exe
cachedir: .pytest_cache
rootdir: D:\开发项目\selenium_test_WeCom
configfile: pytest.ini
plugins: allure-pytest-2.13.5, ordering-0.6, xdist-3.6.1
collecting … [[‘Tony’, ‘test_01’, ‘12345678899’, ‘1585736247@qq.com’]]
collected 1 item

test_wecom.py::TestWeCom::test_add_member[Tony-test_01-12345678899-1585736247@qq.com] ERROR

=============================================== ERRORS ===============================================
______ ERROR at setup of TestWeCom.test_add_member[Tony-test_01-12345678899-1585736247@qq.com] _______

self = <class ‘selenium_test_WeCom.test_case.test_wecom.TestWeCom’>

def setup_class(self):
    """
    复用 cookie 登录
    1、登录成功后,获取 cookies 信息
    2、将 cookies 存入本地文件
    3、从本地获取 cookies
    4、为 self.driver 实例植入 cookie
    5、退出
    6、复用 cookie 登录
    :return:
    """
  self.home = LoginPage().reuse_cookies_login()

test_wecom.py:30:


self = <page_obj.login_page.LoginPage object at 0x000002518D2C1610>, base_driver = None

def __init__(self, base_driver=None):

D:\开发项目\selenium_test_WeCom\test_case>pytest test_wecom.py --browser=chrome
通过命令行获取到的浏览器名称为{‘browser’: ‘chrome’}
{‘browser’: ‘chrome’}
======================================== test session starts =========================================
platform win32 – Python 3.12.0, pytest-8.3.3, pluggy-1.5.0 – D:\python3\python.exe
cachedir: .pytest_cache
rootdir: D:\开发项目\selenium_test_WeCom
configfile: pytest.ini
plugins: allure-pytest-2.13.5, ordering-0.6, xdist-3.6.1
collecting … [[‘Tony’, ‘test_01’, ‘12345678899’, ‘1585736247@qq.com’]]
collected 1 item

test_wecom.py::TestWeCom::test_add_member[Tony-test_01-12345678899-1585736247@qq.com] ERROR

=============================================== ERRORS ===============================================
______ ERROR at setup of TestWeCom.test_add_member[Tony-test_01-12345678899-1585736247@qq.com] _______

self = <class ‘selenium_test_WeCom.test_case.test_wecom.TestWeCom’>

def setup_class(self):
    """
    复用 cookie 登录
    1、登录成功后,获取 cookies 信息
    2、将 cookies 存入本地文件
    3、从本地获取 cookies
    4、为 self.driver 实例植入 cookie
    5、退出
    6、复用 cookie 登录
    :return:
    """
  self.home = LoginPage().reuse_cookies_login()

test_wecom.py:30:


self = <page_obj.login_page.LoginPage object at 0x000002083D48F890>, base_driver = None

def __init__(self, base_driver=None):
    """
    初始化
    1、使用命令行参数、兼容多种类型的浏览器运行程序
    2、实例化 self.driver,并复用浏览器
    3、为每个页面设置 3 秒隐式等待
    4、最大化浏览器窗口
    5 、访问基地址 (访问企业微信登录页)
    :param base_driver: 实例化类时,自带的实例化过的driver对象
    """
    self.browser = None
    if base_driver:
        self.driver = base_driver
    else:

========================================== 1 error in 0.20s ==========================================

D:\开发项目\selenium_test_WeCom\test_case>pytest test_wecom.py --browser=chrome
通过命令行获取到的浏览器名称为{‘browser’: ‘chrome’}
{‘browser’: ‘chrome’}
======================================== test session starts =========================================
platform win32 – Python 3.12.0, pytest-8.3.3, pluggy-1.5.0 – D:\python3\python.exe
cachedir: .pytest_cache
rootdir: D:\开发项目\selenium_test_WeCom
configfile: pytest.ini
plugins: allure-pytest-2.13.5, ordering-0.6, xdist-3.6.1
collecting … [[‘Tony’, ‘test_01’, ‘12345678899’, ‘1585736247@qq.com’]]
collected 1 item

test_wecom.py::TestWeCom::test_add_member[Tony-test_01-12345678899-1585736247@qq.com] ERROR

=============================================== ERRORS ===============================================
______ ERROR at setup of TestWeCom.test_add_member[Tony-test_01-12345678899-1585736247@qq.com] _______

self = <class ‘selenium_test_WeCom.test_case.test_wecom.TestWeCom’>

def setup_class(self):
    """
    复用 cookie 登录
    1、登录成功后,获取 cookies 信息
    2、将 cookies 存入本地文件