【问题标题】:How do I access the command line input in pytest conftest from the pytest_addoptions and use it in fixture params?如何从 pytest_addoptions 访问 pytest conftest 中的命令行输入并在夹具参数中使用它?
【发布时间】:2018-02-15 17:12:03
【问题描述】:

在 pytest 中运行测试时,我有一个 conftest 文件来处理 selenium 驱动程序的设置和拆卸。我正在尝试添加一个命令行选项来确定我是运行本地内置的 selenium 和 web 驱动程序还是远程 selenium 服务器和驱动程序等...

我添加了一个名为“runenv”的命令行选项,我正在尝试从中获取通过命令行输入的字符串值,以确定系统是否应该运行本地或远程 webdriver 配置。这允许测试人员在他们自己的机器上进行本地开发,但也意味着我们可以编写测试脚本以作为构建管道的一部分在远程机器上运行。

我遇到的问题是下面文件中显示的 parser.addoption 未处理。它似乎没有返回我可以使用的值(无论是默认值还是通过命令行传递的值)。

我的 conftest.py 文件如下(*注意 url 和远程 IP 只是示例以涵盖公司隐私)

#conftest.py

import pytest
import os
import rootdir_ref
import webdriverwrapper
from webdriverwrapper import DesiredCapabilities, FirefoxProfile



#when running tests from command line we should be able to pass --url=www..... for a different website, check what order these definitions need to be in
def pytest_addoption(parser):
    parser.addoption("--url", action="store", default="https://mydomain1.com.au")
    parser.addoption("--runenv", action="store", default="local")

@pytest.fixture(scope='session')
def url(request):
     return request.config.option.url

@pytest.fixture(scope='session')
def runenv(request):
     return request.config.option.runenv

BROWSERS = {}


if runenv == 'remote':
    BROWSERS = {'chrome_remote': DesiredCapabilities.CHROME}
else:
    BROWSERS = {'chrome': DesiredCapabilities.CHROME}



# BROWSERS = {
#     #'firefox': DesiredCapabilities.FIREFOX,
#     # 'chrome': DesiredCapabilities.CHROME,
#      'chrome_remote': DesiredCapabilities.CHROME,
#     # 'firefox_remote': DesiredCapabilities.FIREFOX
# }

@pytest.fixture(scope='function', params=BROWSERS.keys())
def browser(request):

    if request.param == 'firefox':
        firefox_capabilities = BROWSERS[request.param]
        firefox_capabilities['marionette'] = True
        firefox_capabilities['acceptInsecureCerts'] = True
        theRootDir = os.path.dirname(rootdir_ref.__file__)
        ffProfilePath = os.path.join(theRootDir, 'DriversAndTools', 'FirefoxSeleniumProfile')
        geckoDriverPath = os.path.join(theRootDir, 'DriversAndTools', 'geckodriver.exe')
        profile = FirefoxProfile(profile_directory=ffProfilePath)
       #  Testing with local Firefox Beta 56
        binary = 'C:\\Program Files\\Mozilla Firefox\\firefox.exe'
        b = webdriverwrapper.Firefox(firefox_binary=binary, firefox_profile=profile, capabilities=firefox_capabilities,
                                     executable_path=geckoDriverPath)

    elif request.param == 'chrome':
        desired_cap = BROWSERS[request.param]
        desired_cap['chromeOptions'] = {}
        desired_cap['chromeOptions']['args'] = ['--disable-plugins', '--disable-extensions']
        desired_cap['browserName'] = 'chrome'
        desired_cap['javascriptEnabled'] = True
        theRootDir = os.path.dirname(rootdir_ref.__file__)
        chromeDriverPath = os.path.join(theRootDir, 'DriversAndTools', 'chromedriver.exe')
        b = webdriverwrapper.Chrome(chromeDriverPath, desired_capabilities=desired_cap)

    elif request.param == 'chrome_remote':
        desired_cap = BROWSERS[request.param]
        desired_cap['chromeOptions'] = {}
        desired_cap['chromeOptions']['args'] = ['--disable-plugins', '--disable-extensions']
        desired_cap['browserName'] = 'chrome'
        desired_cap['javascriptEnabled'] = True
        b = webdriverwrapper.Remote(command_executor='http://192.168.1.1:4444/wd/hub', desired_capabilities=desired_cap)

    elif request.param == 'firefox_remote':
        firefox_capabilities = BROWSERS[request.param]
        firefox_capabilities['marionette'] = True
        firefox_capabilities['acceptInsecureCerts'] = True
        firefox_capabilities['browserName'] = 'firefox'
        firefox_capabilities['javascriptEnabled'] = True
        theRootDir = os.path.dirname(rootdir_ref.__file__)
        ffProfilePath = os.path.join(theRootDir, 'DriversAndTools', 'FirefoxSeleniumProfile')
        profile = FirefoxProfile(profile_directory=ffProfilePath)
        b = webdriverwrapper.Remote(command_executor='http://192.168.1.1:4444/wd/hub',
                                    desired_capabilities=firefox_capabilities, browser_profile=profile)

    else:
        b = BROWSERS[request.param]()
    request.addfinalizer(lambda *args: b.quit())

    return b


@pytest.fixture(scope='function')
def driver(browser, url):
    driver = browser
    driver.set_window_size(1260, 1080)
    driver.get(url)
    return driver

在 conftest 设置页面后,我的测试将简单地使用生成的“驱动程序”夹具。示例测试可能:

import pytest
from testtools import login, dashboard, calendar_helper, csvreadtool, credentials_helper
import time

@pytest.mark.usefixtures("driver")
def test_new_appointment(driver):

    testId = 'Calendar01'
    credentials_list = credentials_helper.get_csv_data('LoginDetails.csv', testId)

    # login
    assert driver.title == 'Patient Management cloud solution'
    rslt = login.login_user(driver, credentials_list)
.... etc..

然后我想使用如下命令运行测试套件: python -m pytest -v --html=.\Results\testrunX.html --self-contained-html --url=https://myotherdomain.com.au/ --runenv=chrome_remote

到目前为止,url 命令行选项有效,我可以使用它来覆盖 url 或让它使用默认值。

但我无法从 runenv 命令行选项中获取值。在下面的 if 语句中,它将始终默认为 else 语句。 runenv 似乎没有值,即使我对该 parser.addoption 的默认值是 'local'

if runenv == 'remote':
    BROWSERS = {'chrome_remote': DesiredCapabilities.CHROME}
else:
    BROWSERS = {'chrome': DesiredCapabilities.CHROME}

我尝试在 if 语句之前放入 pdb.trace() 以便我可以看到 runenv 中的内容,但它只会告诉我它是一个函数,我似乎无法从中获取值,这让我觉得它根本没有被填充。

我不太确定如何调试 conftest 文件,因为输出通常不会出现在控制台输出中。有什么建议? pytest_addoption 实际上是否接受 2 个或更多自定义命令行参数?

我正在使用 Python 3.5.3 pytest 3.2.1 在 Windows 10 上的 VirtualEnv 中

【问题讨论】:

    标签: python parameters pytest fixtures


    【解决方案1】:

    这里,你为什么要把urlrunenv 作为固定装置?你可以像下面这样使用它:

    在你的 conftest.py 中

    def pytest_addoption(parser):
        parser.addoption('--url', action='store', default='https://mytestdomain.com.au/', help='target machine url')
        parser.addoption('--runenv', action='store', default='remote', help='select remote or local')
    
    def pytest_configure(config):
            os.environ["url"] = config.getoption('url') 
            os.environ["runenv"] = config.getoption('runenv') 
    

    现在,无论您想在哪里访问urlrunenv,您只需要写os.getenv('Variable_name') 之类的,

    @pytest.fixture(scope='function')
    def driver(browser):
        driver = browser
        driver.set_window_size(1260, 1080)
        driver.get(os.getenv('url'))
        return driver
    

    或者像你的代码一样,

    if os.getenv('runenv')== 'remote':
        BROWSERS = {'chrome_remote': DesiredCapabilities.CHROME}
    else:
        BROWSERS = {'chrome': DesiredCapabilities.CHROME}
    

    这里,url 和 runenv 将被保存在 OS 环境变量中,您可以在任何地方访问它,而无需通过 os.getenv() 进行固定。

    希望对你有帮助!!

    【讨论】:

    • 感谢@Chanda,太棒了!非常感谢。有机会我会尽快尝试的。
    • 这个答案正是我所需要的。非常感谢。
    • pytest 提供了一种使用固定装置的方法,我真的认为使用全局变量不是这种方法。
    【解决方案2】:

    BROWSERS 填充在conftest.py import 并且在导入时runenv 是一个函数。如果你想使用runenv作为夹具BROWSERS也必须是夹具:

    @pytest.fixture(scope='session')
    def BROWSERS(runenv):
        if runenv == 'remote':
            return {'chrome_remote': DesiredCapabilities.CHROME}
        else:
            return {'chrome': DesiredCapabilities.CHROME}
    

    【讨论】:

    • 谢谢@phd,我确实尝试将 BROWSERS 变成一个固定函数,但后来我在使用参数的浏览器函数中遇到了问题:@pytest.fixture(scope='function', params=BROWSERS.keys()) 其次是def browser(request): 我已经知道我不能在声明它的同一文件中使用装饰夹具的输出,因为名称引用了函数声明。根据此处的文档,我认为我应该能够在夹具定义docs.pytest.org/en/latest/builtin.html#fixtures-and-requests 中使用“名称”,但不能完全解决
    【解决方案3】:

    好的,所以在进行概念验证之后,我的问题的主要部分似乎是我不能使用命令行选项来更改函数的输出(固定或非固定函数)然后使用作为另一个请求夹具功能上的动态参数的列表。在阅读了它之后,它似乎与加载夹具功能期间的处理顺序有关。除了玩元函数之外,我几乎尝试了所有方法。

    我尝试使用所有各种 pytest.mark.fixture 或 params = 部分中的任何变体,它们根本不会产生可迭代的列表(在某些情况下,我可以得到它来给我整个列表但不能迭代它)

    我也尝试了lazyfixture模型,但没有成功。

    我尝试在一个fixture 函数中使用字典作为输出。我在函数之外尝试了它们,我在类中尝试了同样的事情,并在fixture function 中创建了填充对象。我尝试在 params = 中使用 pytest.mark.getfixturevalue,我尝试使用 pytest.mark.use 装饰器,我尝试了 paramatize 装饰器。这些都不起作用。

    看起来唯一可行的方法是此处提出的替代解决方案,但实际上尚未开发。 https://docs.pytest.org/en/latest/proposals/parametrize_with_fixtures.html

    最后我决定将所有逻辑包含在一个大型夹具函数中,这似乎暂时可以工作,但不是我想要的理想方式,因为不幸的是我不能基于命令行条目获得可变参数我要测试哪些浏览器。我必须手动更新 conftest 文件,以判断我是否运行一个或两个浏览器,并让它在每个测试中遍历这两个浏览器。

    # conftest.py
    
    import pytest
    import os
    import rootdir_ref
    import webdriverwrapper
    from webdriverwrapper import DesiredCapabilities, FirefoxProfile
    
    
    # when running tests from command line we should be able to pass --url=www..... for a different website, check what order these definitions need to be in
    def pytest_addoption(parser):
        parser.addoption('--url', action='store', default='https://mytestdomain.com.au/', help='target machine url')
        parser.addoption('--runenv', action='store', default='remote', help='select remote or local')
    
    
    @pytest.fixture(scope='session')
    def url(request):
        return request.config.getoption('url')
    
    
    @pytest.fixture(scope='session')
    def runenv(request):
        return request.config.getoption('runenv')
    
    BROWSERS = {
        # 'firefox': DesiredCapabilities.FIREFOX,
        'chrome': DesiredCapabilities.CHROME
    }
    
    @pytest.fixture(scope='function', params=BROWSERS.keys())
    def browser(request, runenv):
        if request.param == 'firefox':
    
            if runenv == 'local':
                firefox_capabilities = BROWSERS[request.param]
                firefox_capabilities['marionette'] = True
                firefox_capabilities['acceptInsecureCerts'] = True
                theRootDir = os.path.dirname(rootdir_ref.__file__)
                ffProfilePath = os.path.join(theRootDir, 'DriversAndTools', 'FirefoxSeleniumProfile')
                geckoDriverPath = os.path.join(theRootDir, 'DriversAndTools', 'geckodriver.exe')
                profile = FirefoxProfile(profile_directory=ffProfilePath)
                #  Testing with local Firefox Beta 56
                binary = 'C:\\Program Files\\Mozilla Firefox\\firefox.exe'
                b = webdriverwrapper.Firefox(firefox_binary=binary, firefox_profile=profile, capabilities=firefox_capabilities,
                                             executable_path=geckoDriverPath)
            elif runenv == 'remote':
                request.param == 'firefox_remote'
                firefox_capabilities = BROWSERS[request.param]
                firefox_capabilities['marionette'] = True
                firefox_capabilities['acceptInsecureCerts'] = True
                firefox_capabilities['browserName'] = 'firefox'
                firefox_capabilities['javascriptEnabled'] = True
                theRootDir = os.path.dirname(rootdir_ref.__file__)
                ffProfilePath = os.path.join(theRootDir, 'DriversAndTools', 'FirefoxSeleniumProfile')
                profile = FirefoxProfile(profile_directory=ffProfilePath)
                b = webdriverwrapper.Remote(command_executor='https://selenium.mytestserver.com.au/wd/hub',
                                            desired_capabilities=firefox_capabilities, browser_profile=profile)
            else:
                b = webdriverwrapper.Firefox()
        elif request.param == 'chrome':
            if runenv == 'local':
                desired_cap = BROWSERS[request.param]
                desired_cap['chromeOptions'] = {}
                desired_cap['chromeOptions']['args'] = ['--disable-plugins', '--disable-extensions']
                desired_cap['browserName'] = 'chrome'
                desired_cap['javascriptEnabled'] = True
                theRootDir = os.path.dirname(rootdir_ref.__file__)
                chromeDriverPath = os.path.join(theRootDir, 'DriversAndTools', 'chromedriver.exe')
                b = webdriverwrapper.Chrome(chromeDriverPath, desired_capabilities=desired_cap)
            elif runenv == 'remote':
                desired_cap = BROWSERS[request.param]
                desired_cap['chromeOptions'] = {}
                desired_cap['chromeOptions']['args'] = ['--disable-plugins', '--disable-extensions']
                desired_cap['browserName'] = 'chrome'
                desired_cap['javascriptEnabled'] = True
                b = webdriverwrapper.Remote(command_executor='https://selenium.mytestserver.com.au/wd/hub',
                                            desired_capabilities=desired_cap)
            else:
                b = webdriverwrapper.Chrome()
        else:
            b = webdriverwrapper.Chrome()
        request.addfinalizer(lambda *args: b.quit())
    
        return b
    
    
    @pytest.fixture(scope='function')
    def driver(browser, url):
        driver = browser
        driver.set_window_size(1260, 1080)
        driver.get(url)
        return driver
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-25
      • 1970-01-01
      • 2023-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多