【问题标题】:pytest -> How to use fixture return value in test method under a classpytest -> 如何在类下的测试方法中使用夹具返回值
【发布时间】:2016-06-11 05:39:47
【问题描述】:

我有一个夹具,它返回如下值:

import pytest

@pytest.yield_fixture(scope="module")
def oneTimeSetUp(browser):
    print("Running one time setUp")
    if browser == 'firefox':
        driver = webdriver.Firefox()
        print("Running tests on FF")
    else:
        driver = webdriver.Chrome()
        print("Running tests on chrome")
    yield driver
    print("Running one time tearDown")

这个夹具从另一个正在读取命令行选项的夹具获取浏览器值。

然后我有一个测试类,其中我有多个测试方法,它们都希望使用相同的返回值驱动程序来进行测试。

import pytest

@pytest.mark.usefixtures("oneTimeSetUp")
class TestClassDemo():

    def test_methodA(self):
        # I would like to use the driver value here
        # How could I do this?
        # Something like this
        self.driver.get("https://www.google.com")
        self.driver.find_element(By.ID, "some id")
        print("Running method A")

    def test_methodB(self):
        print("Running method B")

使用 self.driver 失败并显示错误消息

self = <test_class_demo.TestClassDemo object at 0x102fb6c18>

    def test_methodA(self):
>       self.driver.get("https://www.google.com")
E           AttributeError: 'TestClassDemo' object has no attribute 'driver'

我知道我可以将夹具作为参数传递给我想使用它的每个方法,但这不是最好的方法,因为我在每个方法中都需要它,并且应该可以将它传递给类然后在所有的测试方法中使用它。

使驱动程序对象可用于方法的最佳方法是什么?

编辑 1:

按照建议在 conftest.py 中创建夹具

@pytest.yield_fixture(scope="class") # <-- note class scope
def oneTimeSetUp(request, browser): # <-- note the additional `request` param
    print("Running one time setUp")
    if browser == 'firefox':
        driver = webdriver.Firefox()
        driver.maximize_window()
        driver.implicitly_wait(3)
        print("Running tests on FF")
    else:
        driver = webdriver.Chrome()
        print("Running tests on chrome")

    ## add `driver` attribute to the class under test -->
    if request.cls is not None:
        request.cls.driver = driver
    ## <--

    yield driver
    print("Running one time tearDown")

我还有一个类,TestClassDemo 中需要哪个对象,我需要将相同的驱动程序实例传递给该类。将其视为 ABC 类

class ABC():

    def __init(self, driver):
        self.driver = driver

    def enterName(self):
        # Do something with driver instance

然后在TestClassDemo中

@pytest.mark.usefixtures("oneTimeSetUp", "setUp")
class TestClassDemo(unittest.TestCase):

    # I need to create an object of class ABC, so that I can use it here
    # abc = ABC(self.driver)

    @pytest.fixture(scope="class", autouse=True)
    def setup(self):
        self.abc = ABC(self.driver)
    # I tried this, but it's not working
    # This error message shows up
    # AttributeError: 'TestClassDemo' object has no attribute 'driver'

    def setup_module(self):
    self.abc = ABC(self.driver)
    # This also does not work
    # Error message ->  AttributeError: 'TestClassDemo' object has no attribute 'abc'


    def test_methodA(self):
        self.driver.get("https://google.com")
        self.abc.enterName("test")
        print("Running method A")

    def test_methodB(self):
        self.abc.enterName("test")
        print("Running method B")

这个 abc 对象应该也可以在其他 test_ 方法中使用。

所有这些类都在单独的模块中,我的意思是在单独的 .py 文件中。

另外请在答案中解释什么是最好的使用方法而不是 yield 驱动程序实例。

编辑 2:

对于这个没有 yield 的示例,运行 oneTimeTearDown 的最佳方法是什么?我在 yield 之后运行了 tearDown 步骤

@pytest.fixture(scope="class")
def oneTimeSetUp(request, browser):
    print("Running one time setUp")
    if browser == 'firefox':
        driver = webdriver.Firefox()
        driver.maximize_window()
        driver.implicitly_wait(3)
        print("Running tests on FF")
    else:
        driver = webdriver.Chrome()
        print("Running tests on chrome")

    if request.cls is not None:
        request.cls.driver = driver

我也尝试过使用 UnitTest 类,但是当我使用 def setUpClass(cls) 时,我无法使用在 test_ 方法中实例化的对象。所以我无法弄清楚如何实现这一目标。

我还想从命令行提供命令行参数,例如浏览器,当我尝试 unittest 时,我必须在每个类中编写命令行参数。我只想在一个地方提供它们,比如一个测试套件。所以conftest在这里帮助了我。

我有一个关于 stackoverflow 的问题,但没有得到回复。你也可以看看吗? Python unittest passing arguments to parent test class

谢谢

谢谢

【问题讨论】:

    标签: python python-3.x automated-tests pytest python-unittest


    【解决方案1】:

    py.text unittest integration documentation 中概述了一种可能对您有帮助的技术……使用内置的request 固定装置。否则,我不知道在不提供命名夹具作为方法参数的情况下访问夹具返回值的方法。

    @pytest.yield_fixture(scope="class") # <-- note class scope
    def oneTimeSetUp(request, browser): # <-- note the additional `request` param
        print("Running one time setUp")
        if browser == 'firefox':
            driver = webdriver.Firefox()
            print("Running tests on FF")
        else:
            driver = webdriver.Chrome()
            print("Running tests on chrome")
    
        ## add `driver` attribute to the class under test -->
        if request.cls is not None:
            request.cls.driver = driver
        ## <--
    
        yield driver
        print("Running one time tearDown")
    

    现在您可以将driver 作为TestClassDemo 中的类属性访问,就像您在示例中所做的那样(即self.driver 应该可以工作)。

    需要注意的是,您的夹具必须使用scope='class',否则request 对象将不具有cls 属性。

    希望对你有帮助!


    更新

    我还有一个类,TestClassDemo 中需要哪个对象,我需要将相同的驱动程序实例传递给该类。将其视为 ABC 类

    如果没有更多上下文就很难知道,但在我看来,您可能可以在实例化 ABC 对象的同时避免实例化 driver ... 在 oneTimeSetUp 夹具中。比如……

    @pytest.yield_fixture(scope="class")
    def oneTimeSetUp(request, browser):
        print("Running one time setUp")
        if browser == 'firefox':
            driver = webdriver.Firefox()
            driver.maximize_window()
            driver.implicitly_wait(3)
            print("Running tests on FF")
        else:
            driver = webdriver.Chrome()
            print("Running tests on chrome")
    
        if request.cls is not None:
            request.cls.driver = driver
            request.cls.abc = ABC(driver) # <-- here
    
        yield driver
        print("Running one time tearDown")
    

    但如果你只需要一个或两个测试类的 ABC 实例,下面是你可以如何在类定义中使用固定装置...

    @pytest.mark.usefixtures("oneTimeSetUp", "setUp")
    class TestClassDemo(unittest.TestCase):
        @pytest.fixture(autouse=True)
        def build_abc(self, oneTimeSetUp): # <-- note the oneTimeSetup reference here
            self.abc = ABC(self.driver)
    
        def test_methodA(self):
            self.driver.get("https://google.com")
            self.abc.enterName("test")
            print("Running method A")
    
        def test_methodB(self):
            self.abc.enterName("test")
            print("Running method B")
    

    我不会对第二个例子特别满意。第三种选择是拥有另一个完全独立于oneTimeSetUp 的yield_fixture 或类似的,并返回一个已包装驱动程序的ABC 实例。

    哪种方式最适合您?没有把握。您需要根据自己的工作来决定。

    为后代注意 pytest 固定装置只是糖和一点魔法是正确的。如果您发现它们很困难,则根本不需要使用它们。 pytest 很高兴执行 vanilla unittest TestCases。


    另外请在答案中解释什么是最好的使用方法而不是屈服驱动程序实例。

    这就是我的想法......

    @pytest.fixture(scope="class")
    def oneTimeSetUp(request, browser):
        print("Running one time setUp")
        if browser == 'firefox':
            driver = webdriver.Firefox()
            driver.maximize_window()
            driver.implicitly_wait(3)
            print("Running tests on FF")
        else:
            driver = webdriver.Chrome()
            print("Running tests on chrome")
    
        if request.cls is not None:
            request.cls.driver = driver
    

    ...请注意,这不会返回(或产生)驱动程序对象,这意味着将这个夹具作为命名参数提供给函数/方法不再有用,如果您的所有测试用例被编写为类(由您的示例建议)。

    但是,如果您想将夹具用作命名参数,请不要这样做。

    【讨论】:

    • 非常感谢您的解决方案,我有几个后续问题。您能否编辑您的答案以解释最后一行? “如果您不打算在其他测试函数中使用 oneTimeSetUp 夹具(例如,通过命名参数),那么当然可以进一步简化上述代码。”一个重要的问题:如果我想在我的 TestClassDemo 中初始化另一个类的对象,那么我应该在哪里初始化它?我还需要将驱动程序传递给另一个类的对象。请帮忙,你的回答太棒了。我真的很努力地完成了很多天
    • 应该在TestClassDemo中有__init__()来创建另一个类的对象?是否可以从 __init__() 为该对象提供驱动程序,还是只能在测试方法中使用?
    • 嗨@SunShine,我刚刚删除了最后一句话,因为它的结构很差而且没有多大帮助。澄清一下,我的意思是如果使用request 固定装置对您有用,对于每个 测试,那么您不需要使用yield_fixture 或返回/产生一个值。
    • 对于第二个问题,您可以使用autouse=True 在类定义中创建一个fixture 方法,以设置另一个实例对象以在整个测试类中使用。您还可以使用scope 属性来确定何时创建另一个对象的实例。 (或者,您可以定义设置方法,xunit 样式。如pytest.org/latest/xunit_setup.html)如果您使用另一个对象使用示例更新您的问题,我将更新我的答案以进行演示。
    • 请参阅pytest.org/latest/…,了解在测试类中使用autouse=True 的示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多