Have a object created on a fixture accassible at setup_class, teardown_class, setup_method, and teardown_method

huangapple go评论63阅读模式
英文:

Have a object created on a fixture accassible at setup_class, teardown_class, setup_method, and teardown_method

问题

我有一个 conftest.py 文件和一个测试文件(test_1.py)。下面是你的要求:

  1. library_instancesetup_classteardown_classsetup_method,和 teardown_method 中都可调用,而不仅仅是测试用例中可用的。
  2. teardown_method 中检查测试是否失败,如果失败则调用一些函数。

你需要的代码如下:

import pytest
import asyncio
from some_library import MyLibrary

@pytest.fixture()
def event_loop(request):
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()

@pytest.fixture(name="library_instance", scope='session')
def fixture_library_instance():
    library = MyLibrary()

    print("\nSetup Library")
    library.config()

    yield library

    print("\nTeardown Library")
    library = None

@pytest.fixture(scope="class")
def library_in_class(library_instance):
    yield library_instance

class Test_Somemore:
    @classmethod
    def setup_class(cls):
        print("\nSetup Class")
        cls.library_instance.foo1()

    @classmethod
    def teardown_class(cls):
        print("\nTeardown Class")
        cls.library_instance.foo2()

    @classmethod
    def setup_method(cls, method):
        print("\nSetup Test =", method.__name__)
        cls.library_instance.foo3()

    @classmethod
    def teardown_method(cls, method):
        print("\nTeardown Test =", method.__name__)
        if method._outcome.errors:
            cls.library_instance.foo4()

    @pytest.mark.usefixtures("library_in_class")
    @pytest.mark.asyncio
    async def test_something_4(self):
        print(f"\ntest 4 - {self.library_instance.var}")
        assert 1 == 1
        assert self.library_instance.var == 100

    @pytest.mark.usefixtures("library_in_class")
    @pytest.mark.asyncio
    async def test_something_5(self):
        print(f"\ntest 5 - {self.library_instance.var}")
        assert 2 == 2
        assert self.library_instance.var == 100

    @pytest.mark.usefixtures("library_in_class")
    @pytest.mark.asyncio
    async def test_something_6(self):
        print(f"\ntest 6 - {self.library_instance.var}")
        assert 3 == 3
        assert self.library_instance.var == 100

上述代码中,我添加了一个名为 library_in_class 的新的 fixture,用于在测试类中共享 library_instance。在 setup_classteardown_classsetup_methodteardown_method 中,你可以直接使用 self.library_instance。在 teardown_method 中,我检查了测试是否失败,并根据需要调用了相应的函数。

英文:

I have a conftest.py:

import pytest
import asyncio
from some_library import MyLibrary

@pytest.fixture()
def event_loop(request):
    loop = asyncio.get_event_loop_policy().new_event_loop()
    yield loop
    loop.close()
    
@pytest.fixture(name="library_instance", scope='session')
def fixture_library_instance():
    library = MyLibrary()
    
    print("\nSetup Library")
    library.config()
    
    yield library

    print("\nTeardown Library")
    library = None

And a test file (test_1.py):

import pytest

class Test_Somemore:
    @classmethod
    def setup_class(self):
        print("\nSetup Class")
        
    @classmethod
    def teardown_class(self):
        print("\nTeardown Class")

    @classmethod
    def setup_method(self, method):
        print("\nSetup Test = ", method.__name__)

    @classmethod
    def teardown_method(self, method):
        print("\nTeardown Test = ", method.__name__)

    @pytest.mark.usefixtures("library_instance")
    @pytest.mark.asyncio
    async def test_something_4(self, library_instance):
        print(f"\ntest 4 - {library_instance.var}")
        assert 1 == 1
        assert library_instance.var == 100
    
    @pytest.mark.usefixtures("library_instance")
    @pytest.mark.asyncio
    async def test_something_5(self, library_instance):
        print(f"\ntest 5 - {library_instance.var}")
        assert 2 == 2
        assert library_instance.var == 100

    @pytest.mark.usefixtures("library_instance")
    @pytest.mark.asyncio
    async def test_something_6(self, library_instance):
        print(f"\ntest 6 - {library_instance.var}")
        assert 3 == 3
        assert library_instance.var == 100

The order it is being called is:

  1. Setup Library
  2. Setup Class
  3. Setup Test (test 4)
  4. Teardown Test (test 4)
  5. Setup Test (test 5)
  6. Teardown Test (test 5)
  7. Setup Test (test 6)
  8. Teardown Test (test 6)
  9. Teardown Class
  10. Teardown Libary

This is ok.

What I need is the following:

  1. To have library_instance (from the fixture "fixture_library_instance") callable inside the setup_class, teardown_class, setup_method, and teardown_method. Not just the test cases. I haven't found a way to make this work.
  2. In teardown_method, check if the test has failed. If it did, I want to call some functions.

Basically something like this:

    @classmethod
    def setup_class(self):
        print("\nSetup Class")
        library_instance.foo1()
        
    @classmethod
    def teardown_class(self):
        print("\nTeardown Class")
        library_instance.foo2()

    @classmethod
    def setup_method(self, method):
        print("\nSetup Test = ", method.__name__)
        library_instance.foo3()

    @classmethod
    def teardown_method(self, method):
        print("\nTeardown Test = ", method.__name__)
        if test == FAIL:
            library_instance.foo4()

Can somebody please help me?

答案1

得分: 1

你的要求都是可以实现的。

第一个要求(在setup方法中访问另一个fixture)如果你将你的setup/teardown方法改为fixtures的话,就会很容易。在这种情况下,你可以引用相同或更广泛范围的其他fixtures:

class TestSomemore:
    @classmethod
    @pytest.fixture(scope="class", autouse=True)
    def prepare_class(cls, library_instance):
        print("\nSetup Class")
        library_instance.foo1()
        yield
        print("\nTeardown Class")

    @pytest.fixture(autouse=True)
    def prepare_method(self, request, library_instance):
        print("\nSetup Test =", request.node.name)
        library_instance.foo2()
        yield
        print("\nTeardown Test =", request.node.name)

一些备注:

  • 我本可以保留魔术名称 setup_classsetup_method,但为了说明我将它们改变了。一旦你将它们变成fixtures,名称就不重要了。
  • 为了自动调用fixtures,你必须设置 autouse=True(这很明显,但我还是想提一下)。
  • setup_method 中传递 method 参数是不会起作用的(它将被视为fixture名称),但你可以通过 request fixture 来获取名称。

使用fixture表示法可以说是比分开的setup/teardown方法更可取,因为它只需要一个方法,并且还允许在setup和teardown(在 yield 之前和之后)中使用相同的局部变量,尽管这也是品味的问题。

至于第二部分(检查fixture是否失败)- 这已经在这里很好地回答了,并附有链接到相关文档,所以我就不在这里重复了。

英文:

Both your requirements are possible.
The first one (accessing another fixture in a setup method) is trivial if you change your setup/teardown methods to fixtures. In this case, you can just reference other fixtures of the same or a wider scope:

class TestSomemore:
    @classmethod
    @pytest.fixture(scope="class", autouse=True)
    def prepare_class(cls, library_instance):
        print("\nSetup Class")
        library_instance.foo1()
        yield
        print("\nTeardown Class")

    @pytest.fixture(autouse=True)
    def prepare_method(self, request, library_instance):
        print("\nSetup Test = ", request.node.name)
        library_instance.foo2()
        yield
        print("\nTeardown Test = ", request.node.name)

A few remarks:

  • I could have retained the magic names setup_class and setup_method but changed them for illustration. As soon as you make them fixtures, the name does not matter.
  • In order to have the fixtures automatically called, you have to set autouse=True (quite obvious, but I wanted to mention it).
  • Passing the method argument as in setup_method won't work (it will be seen as a fixture name), but you can get the name via the request fixture instead.

Using the fixture notation arguably is preferrable to separate setup/teardown methods, as it only needs one method, and also allows the use of the same local variables for both setup and teardown (e.g. before and after the yield), though that is also a matter of taste.

As for the second part (checking if a fixture has failed) - this has been nicely answered here, with a link to the relevant documentation, so I won't repeat it here.

huangapple
  • 本文由 发表于 2023年7月3日 22:19:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/76605629.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定