python - pytest -> 如何在类下的测试方法中使用 fixture 返回值

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

我有一个 fixture 返回这样的值:

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")

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

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

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'

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

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

编辑 1:

按照建议在 conftest.py 中创建 fixture

@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 之后运行了拆解步骤

@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

谢谢

谢谢

最佳答案

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

@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")

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

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

希望对您有所帮助!


更新

I have one more class, which object in need in the TestClassDemo and I need to pass the same driver instance to the class. Consider it as class ABC

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

@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 实例,下面是你可以如何在类定义中使用 fixture ......

@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")

我对第二个例子不是特别满意。第三种选择是使用另一个 yield_fixture 或类似的东西,它与 oneTimeSetUp 完全分开,并返回一个包含驱动程序的 ABC 实例。

哪种方式最适合您?不确定。您需要根据您的工作对象做出决定。

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


Also please explain in the answer what is the best way to use instead of yield driver instance.

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

@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

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

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

关于python - pytest -> 如何在类下的测试方法中使用 fixture 返回值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37760366/

相关文章:

python - 我可以在 PyCharm 中自动格式化文件以使用空格而不是制表符吗?

python - Django 通过对象名称注释,这是一个 ForeignKey

python - Python是否允许调用装饰器工厂?

python - 创建嵌套字典的迭代问题

drag-and-drop - 我在 testcafe 中使用拖动功能时遇到问题

python - Numpy 数组与权重求和

python - 在网站上查找一个单词并获取其页面链接

python - 数据集识别12 :00AM as 00:00AM

python - 使用平面图时,在假设中获取 'LazyStrategy' 对象而不是整数

node.js - 自动浏览器不提交表单