Gao Fushuai in the unit testing world, Pytest framework (2) pre- and post-methods and fixture mechanism

Preface

In the previous article, we introduced the basic use of pytest. This article is dedicated to explaining the pre- and post-processing of use case execution in pytest. The pre- and post-processing of use case execution in pytest can be done through the test fixture (fixtrue). Implementation can also be achieved through xunit-style pre- and post-methods. Next let's take a look at how to use it in detail.

1. xunit style front and back methods

1. Pre and post methods of function use cases

Define use cases in the form of functions in the module. You can use setup_function and teardown_function to define the pre- and post-methods of the function use cases. The use cases are as follows:

def setup_function(function):
    print("函数用例前置方法执行")

  
def teardown_function(function):
    print("函数用例后置方法执行")


def test_01():
    print('----用例方法01------')

operation result:

C:\testcases>pytest -s
========================= test session starts =========================
platform win32 -- Python 3.7.3, pytest-5.4.2, py-1.8.0, pluggy-0.13.0 
cachedir: .pytest_cache
rootdir: C:\testcases
plugins:  testreport-1.1.2
collected 1 item                                                      
test_demo.py
函数用例前置方法执行
----用例方法01------ .
函数用例后置方法执行
========================= 1 passed in 0.27s =========================

2. Pre- and post-methods of use cases in test classes

  • Class-level pre- and post-methods
  • The pre- and post-methods setup_class and teardown_class at the test class level in pytest are executed before the use cases in the test class are executed, and after all use cases in the test class are executed. The specific usage is as follows:
  • class TestDome: def test_01(self): print('----Test case: test_01------') def test_02(self): print('----Test case: test_02----- -') @classmethod def setup_class(cls): print("Test class pre-method ---setup_class---") @classmethod def teardown_class(cls): print("Test class post-method ---teardown_class-- -")
  • Use case level pre- and post-methods
  • The pre- and post-methods setup_method and teardown_method at the use case level in the test class in pytest are executed before the use cases in the test class are executed, and after all use cases in the test class are executed. The specific usage is as follows:
  • class TestDome: def test_01(self): print('----Test case: test_01------') def test_02(self): print('----Test case: test_02----- -') @classmethod def setup_class(cls): print("Test class pre-method ---setup_class---") @classmethod def teardown_class(cls): print("Test class post-method ---teardown_class-- -") def setup_method(function): print("Test case pre-method---setup_method---") def teardown_method(function): print("Test case post-method---teardown_method---")

operation result:

C:\testcases>pytest -s
==================== test session starts ====================
platform win32 -- Python 3.7.3, pytest-5.4.2, py-1.8.0, pluggy-0.13.0
rootdir: C:\testcases
plugins:  testreport-1.1.2
collected 2 items                                                                                                                                 
test_demo.py 
测试类前置方法---setup_class---
测试用例前置方法---setup_method---
----测试用例:test_01------.
测试用例后置方法---teardown_method---
测试用例前置方法---setup_method---
----测试用例:test_02------.
测试用例后置方法---teardown_method---
测试类后置方法---teardown_class---

==================== 2 passed in 0.30s =======================

3. Module-level pre- and post-processing methods

There are also two functions in pytest, setup_module and teardown_module, which are used to set module-level pre- and post-methods. They are defined in the module and will be executed before and after all use cases in the entire module are executed. The specific usage is as follows:


class TestDome:

    def test_01(self):
        print('----测试用例:test_01------')


class TestDome2:

    def test_02(self):
        print('----测试用例:test_02------')
   
  
def setup_module(module):
    print("测试模块的前置方法---setup_module---")


def teardown_module(module):
    print("测试模块的前置方法---teardown_module---")

operation result:

C:\testcases>pytest -s
====================== test session starts ====================== 
platform win32 -- Python 3.7.3, pytest-5.4.2, py-1.8.0, pluggy-0.13.0
rootdir: C:\testcases
plugins:testreport-1.1.2
collected 2 items                                                                   
test_demo.py 测试模块的前置方法---setup_module---
----测试用例:test_01------
.----测试用例:test_02------
.测试模块的前置方法---teardown_module---
====================== 2 passed in 0.27s ====================== 

2. Fixture mechanism

Earlier we introduced the xunit-style front and back methods in pytest. Next, let's take a look at the use of the more powerful fixture mechanism (test fixture) in pytest.

1. Levels and definitions of test fixtures

Test fixtures need to be defined using the pytest.fixture decorator. Test fixtures in pytest have the following levels: use case level, test class level, module level, package level, and session level. Next let's take a look at the fixture definition syntax.

The fixture definition can specify the level of the fixture through the parameter scope. If the fixture level is not specified, the default value of scope is function (use case level)

Use case level: scope = function

Test class level: scope = class

Module level: scope = module

Package level: scope = package

Session level: scope = session

@pytest.fixture(scope='指定夹具的级别')
def work():
    # 前置执行脚本
    yield 
    # 后置执行脚本

The test fixture is essentially a generator function. When the generator function uses next to iterate, it will return data when executed to yeild, pause execution, and wait for the next iteration before continuing execution. The pytest fixture is the mechanism of the generator used. , execute the pre- and post-code separately in the test fixture through yeild.

Note: The fixture can only be used within the scope of the defined fixture. If the fixture is defined in a class, it can only be used by test cases within that class. But if the fixture is defined in the global scope of the module, then every test case in that module, even if it is defined in a class, can use it.

Now that we know how to define a fixture, let's take a look at how to use it.

2. Use of fixtures

After the test fixtures are defined, the test function specifies the fixtures to be executed before executing the test case by declaring them as parameters.

When pytest starts running a test, it looks at the formal parameters defined by the test function and then searches for a test fixture with the same name as those parameters. Once pytest finds them, it runs the fixtures, receives their returns (if any), and passes these returns as arguments to the test function.

Note: When we use the fixture, if the pre-script of the fixture is executed and there is data to be passed to the use case, the data that needs to be passed can be written after yield. In the use case or method of using the fixture, you can pass the defined formal parameters To obtain the data returned by yeild (use cases are introduced in Chapter 2.3)

2.1. Use fixtures in use cases

Whether it is a test case defined in the form of a function or a use case defined in the form of a method in a test class, it is the same when used. Simply define a formal parameter with the same name as the fixture to be used.

import pytest

# 定义一个用例级别的夹具
@pytest.fixture
def my_fixture():
    print('------my_fixture---用例前置执行脚本--------')
    yield
    print('------my_fixture---用例后置执行脚本--------')

# 函数用例 指定测试夹具
def test_func__01(my_fixture):
    print("测试用例----test_func__01----")


class TestDome:
    # 函数用例 指定测试夹具
    def test_02(self, my_fixture):
        print('----测试用例:test_02------')
  
     # 函数用例 指定测试夹具
    def test_03(self):
        print('----测试用例:test_03------')

operation result

C:\testcases>pytest -s
======================== test session starts ========================
platform win32 -- Python 3.7.3, pytest-5.4.2, py-1.8.0, pluggy-0.13.0
rootdir: C:\testcases
plugins: testreport-1.1.2
collected 2 items  
test_demo.py 
------my_fixture---前置执行脚本--------
测试用例----test_func__01----.
------my_fixture---后置执行脚本--------
------my_fixture---前置执行脚本--------
----测试用例:test_02------.
------my_fixture---后置执行脚本--------
----测试用例:test_03------
======================== 2 passed in 0.27s ========================

The above two use cases test_func__01 and test_02 specified test fixtures when they were defined, while test_03 did not specify the fixture for execution. When the use case was executed, the library saw the use case with the fixture specified and executed the corresponding fixture.

2.2. Specify fixtures for test classes and modules

Above we specify the test fixture for a single test case by adding formal parameters to the use case method. If there are many test cases in a test class or many use cases in a module, the same test fixture must be specified. If we want, we can specify the test fixture for the test class or test module through usefixtures.

Specify fixtures for all test cases in the test class

# TestDome这个测试类的所有测试用例均执行my_fixture这个夹具
@pytest.mark.usefixtures('my_fixture这个夹具')
class TestDome:
    # 函数用例 指定测试夹具
    def test_02(self):
        print('----测试用例:test_01------')

    # 函数用例 指定测试夹具
    def test_03(self):
        print('----测试用例:test_02------')

Use pytestmark to specify fixtures for all test case executions of the module at the test module level

# test_demo.py

# 当前模块中所有的用例,均执行my_fixture这个测试夹具
pytestmark = pytest.mark.usefixtures('my_fixture')

# 函数用例 指定测试夹具
def test_func__01(my_fixture):
    print("测试用例————test_func__01——————")

  
class TestDome:
    # 函数用例 指定测试夹具
    def test_02(self):
        print('----测试用例:test_01------')

    # 函数用例 指定测试夹具
    def test_03(self):
        print('----测试用例:test_02------')

2.3. Reference the fixture in the fixture

One of the biggest advantages of pytest is its extremely flexible fixture system. Through test fixtures, we can split extremely complex front and rear dependencies into simpler single-function test fixtures, and organize the complex dependency environments required by different use cases by referencing other fixtures in the fixtures. Next, we will use a case to demonstrate how to use it.

import pytest
# 用户注册的夹具
@pytest.fixture
def register_user():
    print('---用户注册的夹具前置执行----')
    # ...注册代码省略,注册的用户信息如下
    user_info = {'user': 'lemonban', 'pwd': '123456'}
    yield user_info
    print('---用户注册的夹具后置执行----')


# 用户登录的夹具,通过定义形参来使用register_user这个夹具
@pytest.fixture
def user_login(register_user):
    print('---用户登录的夹具前置执行----')
    # 获取register_user结局前置脚本执行完,yeild传递出来的数据
    user_info = register_user
    # ...登录代码省略,下面为登录得到的token
    token = 'sdjasjdask'
    yield token
    print('---用户登录的夹具后置执行----')

# 函数用例 指定使用测试夹具user_login
def test_func__01(user_login):
    token = user_login
    print("测试用例夹具user_login传递过来的token:",token)
    print("测试用例---test_func__01---")

operation result

C:\testcases>pytest -s
======================== test session starts ========================
platform win32 -- Python 3.7.3, pytest-5.4.2, py-1.8.0, pluggy-0.13.0
rootdir: C:\testcases
plugins: testreport-1.1.2
collected 1 item  
test_demo.py 
---用户注册的夹具前置执行----
夹具register_user传递过来的用户信息: {'user': 'lemonban', 'pwd': '123456'}
---用户登录的夹具前置执行----
测试用例夹具user_login传递过来的token: sdjasjdask
测试用例---test_func__01---.
---用户登录的夹具后置执行----
---用户注册的夹具后置执行----

2.4. Automatic use of fixtures

When defining the test fixture, we can add the parameter autouse=True to the fixture's decorator to make the fixture an automatically executed fixture. Specific cases are as follows:

import pytest


@pytest.fixture(autouse=True)
def my_fixture():
    print('------my_fixture---前置执行脚本--------')
    yield
    print('------my_fixture---后置执行脚本--------')


class TestDome:
    # 函数用例 指定测试夹具
    def test_02(self):
        print('----测试用例:test_01------')

    # 函数用例 指定测试夹具
    def test_03(self):
        print('----测试用例:test_02------')


class TestDome2:
    # 函数用例 指定测试夹具
    def test_03(self):
        print('----测试用例:test_03------')

Results of the:

C:\testcases>pytest -s
======================== test session starts ========================
platform win32 -- Python 3.7.3, pytest-5.4.2, py-1.8.0, pluggy-0.13.0
rootdir: C:\testcases
plugins: testreport-1.1.2
collected 3 items    
test_demo.py
------my_fixture---前置执行脚本--------
----测试用例:test_01------.
------my_fixture---后置执行脚本--------
------my_fixture---前置执行脚本--------
----测试用例:test_02------.
------my_fixture---后置执行脚本--------
------my_fixture---前置执行脚本--------
----测试用例:test_03------.
------my_fixture---后置执行脚本--------
======================== 3 passed in 0.29s ========================

From the above execution results, we can see that the test fixture my_fixture is automatically executed before each use case is executed.

3、conftest.py

In the testing of a project, in most cases there will be multiple classes, modules, or packages that use the same test fixture. In this case, if we define the test fixture in a certain module, sharing cannot be achieved. In this case, we can put the test fixtures that need to be shared into a separate conftest.py file, so that multiple test fixtures can be shared. test modules shared

ps: When pytest runs a test, if there is conftest.py in the project, then pytest will automatically load the contents of the conftest.py module. You can use conftest to see the plug-in module that pytest will automatically load. The subsequent tutorials will involve conftest Define pytest’s hooks function in .py

Next, let’s look at a case where conftest.py defines a test fixture.

Define the test fixture my_fixture in conftest.py

# conftest.py

import pytest

@pytest.fixture
def my_fixture():
    print('------my_fixture---前置执行脚本--------')
    yield
    print('------my_fixture---后置执行脚本--------')

The test case in test_demo1.py uses the fixture defined in conftest.py

# test_demo1.py
class TestDome:
    # 函数用例 指定测试夹具
    def test_02(self,my_fixture):
        print('----测试用例:test_01------')

    # 函数用例 指定测试夹具
    def test_03(self,my_fixture):
        print('----测试用例:test_02------')

The test case in test_demo2.py uses the fixture defined in conftest.py

# test_demo2.py
class TestDome2:
    # 函数用例 指定测试夹具
    def test_03(self,my_fixture):
        print('----测试用例:test_03------')

Results of the:

C:\testcases>pytest -s
======================== test session starts ========================
platform win32 -- Python 3.7.3, pytest-5.4.2, py-1.8.0, pluggy-0.13.0
rootdir: C:\testcases
plugins: testreport-1.1.2
collected 3 items
test_demo.py 
------my_fixture---前置执行脚本--------
----测试用例:test_01------.
------my_fixture---后置执行脚本--------
------my_fixture---前置执行脚本--------
----测试用例:test_02------.
------my_fixture---后置执行脚本--------
test_demo2.py 
------my_fixture---前置执行脚本--------
----测试用例:test_03------.
------my_fixture---后置执行脚本--------
======================== 3 passed in 0.29s ========================

In the above case, we can find that the test cases in est_demo.py and est_demo2.py can successfully use the test cases in conftest.py.

The next article will explain to you use case marking and test execution.
Below are some of the materials I used when studying. Friends who need it can leave a message in the comment area.

Guess you like

Origin blog.csdn.net/a448335587/article/details/132778785