pytest Chapter 3 Fixture and scope

pytest Chapter 3 Fixture and scope

Summary: Setup and teardown act globally. If different cases in a test class have different pre and post operations, setup and teardown cannot be used to solve the problem. So we need to customize the preset conditions for testing .

Advantages of fixtures

The advantages of fixtures include the following:

  • The naming method is flexible and clear, and is not limited to setup/teardown.
  • Fixtures are implemented in a modular way, as each fixture name triggers a call to the fixture function, which itself can use other fixtures
  • Data sharing can be achieved in the conftest.py file, and some configurations can be found automatically without import.
  • Allows fixtures and test cases to be parameterized based on configuration and component options, or to reuse the fixture within a test method/class/module or across a test session

Three ways to call fixtures

Three ways to call fixtures:

  1. The function or class method directly passes the fixture function name as a parameter
  2. Use the decorator @pytest.mark.usefixtures()
  3. autouse=True automatically used

The fixture function name is used as a parameter

The function is registered as a fixture function through the @pytest.fixture decorator, and the object returned by the function can be passed into the test method as a parameter.

import pytest
@pytest.fixture()
def get_a():
    print('\n执行fixture函数')

def test001(get_a):
    print('\n执行test001')
    assert True

if __name__ == '__main__':
    pytest.main(['-s','test_b.py'])

pytest execution results:

============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 1 item

test_b.py 
执行fixture函数

执行test001
.

============================== 1 passed in 0.01s ===============================

As in the above example, the fixture function get_a has no return value and is executed first after being passed into test001 as a parameter. The fixture function can have a return value or not. Fixtures allow test methods to easily introduce pre-defined initialization preparation functions without having to worry about the details of import/setup/cleanup methods. This is a prime example of dependency injection, where the functionality of the fixture function acts as an "injector" and the test method "consumes" these fixture objects.

autouse=True automatically used

#test_b.py
import pytest
def test001():
    print('\n执行test001')

def test002():
    print('\n执行test002')

if __name__ == '__main__':
    pytest.main(['-s','test_b.py'])
    
#conftest.py
import pytest
@pytest.fixture(scope="function",autouse=True)
def get_a():
    a=1
    print('\n执行fixture函数>>>>>>>>get_a(function)')
    return a

@pytest.fixture(scope="module",autouse=True)
def get_b():
    b=2
    print('\n执行fixture函数>>>>>>>>get_b(module)')
    return b

pytest execution results:

============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 2 items

test_b.py 
执行fixture函数>>>>>>>>get_b(module)

执行fixture函数>>>>>>>>get_a(function)

执行test001
.
执行fixture函数>>>>>>>>get_a(function)

执行test002
.

============================== 2 passed in 0.01s ===============================

As shown in the above example, after the fixture function uses autouse=True, the test case can be used automatically without passing the fixture function name.

Disadvantages: This currently cannot cope with the situation where the test case needs to call the fixture function to return the value.

Use the decorator @pytest.mark.usefixtures()

You can use the decorator **@pytest.mark.usefixtures()**

#test_b.py
import pytest
@pytest.mark.usefixtures('get_b')
@pytest.mark.usefixtures('get_a')
def test001():
    print('\n执行test001')

@pytest.mark.usefixtures('get_a')
def test002():
    print('\n执行test002')

if __name__ == '__main__':
    pytest.main(['-s','test_b.py'])

#conftest.py
import pytest
@pytest.fixture(scope="function")
def get_a(request):
    a=1
    print('\n执行fixture函数>>>>>>>>get_a(function)')
    return a

@pytest.fixture(scope="module")
def get_b(request):
    b=2
    print('\n执行fixture函数>>>>>>>>get_b(module)')
    return b

pytest execution results:

============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 2 items

test_b.py 
执行fixture函数>>>>>>>>get_b(module)

执行fixture函数>>>>>>>>get_a(function)

执行test001
.
执行fixture函数>>>>>>>>get_a(function)

执行test002
.

============================== 2 passed in 0.01s ===============================

fixture effective range

The fixture uses the scope parameter to indicate 4 effective scopes:

  • function (default): every function or method will be called
  • class: Called once for each class
  • module: called once per py file
  • session: called across py files, called once in one execution

The scope is session>module>class>function

See examples below:

function

import pytest
@pytest.fixture(scope="class")
def get_a():
    a=1
    print('\n执行fixture函数')
    return a

def test001(get_a):
    print('\n执行test001')
    print("获取a:%d" % get_a)
    assert True

def test002(get_a):
    print('\n执行test002')
    print("获取a:%d" % get_a)
    assert True

if __name__ == '__main__':
    pytest.main(['-s','test_b.py'])

pytest execution results:

============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 2 items

test_b.py 
执行fixture函数

执行test001
获取a:1
.
执行fixture函数

执行test002
获取a:1
.

============================== 2 passed in 0.01s ===============================

function: If the function passes the fixture function as a parameter, the fixture function is executed once before the function. It will not be executed if it is not passed in, and it will not be executed if it is passed in the method. It only acts on the function, and it is a function of the same py file.

class

import pytest
@pytest.fixture(scope="class")
def get_a():
    a=1
    print('\n执行fixture函数')
    return a

class TestCase:
    def test001(self,get_a):
        print('\n执行test001')
        print("获取a:%d" % get_a)
        assert True

    def test002(self,get_a):
        print('\n执行test002')
        print("获取a:%d" % get_a)
        assert True

if __name__ == '__main__':
    pytest.main(['-s','test_b.py'])

pytest execution results:

============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 2 items

test_b.py 
执行fixture函数

执行test001
获取a:1
.
执行test002
获取a:1
.

============================== 2 passed in 0.02s ===============================

class: All methods in the class pass in the fixture function, and the fixture function is executed once at the beginning of the class.

import pytest
@pytest.fixture(scope="class")
def get_a():
    a=1
    print('\n执行fixture函数')
    return a

class TestCase:
    def test001(self):
        print('\n执行test001')
        assert True

    def test002(self,get_a):
        print('\n执行test002')
        print("获取a:%d" % get_a)
        assert True

    def test003(self):
        print('\n执行test003')
        assert True

    def test004(self,get_a):
        print('\n执行test004')
        print("获取a:%d" % get_a)
        assert True

if __name__ == '__main__':
    pytest.main(['-s','test_b.py'])

pytest execution results:

============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 4 items

test_b.py 
执行test001
.
执行fixture函数

执行test002
获取a:1
.
执行test003
.
执行test004
获取a:1
.

============================== 4 passed in 0.02s ===============================

class: If the fixture function is not passed in to all methods in the class, it will be executed once before the first method passed into the fixture function is executed, and will not be executed before the other methods. (That is to say, regardless of whether there are one or more methods passed into the fixture function, the fixture function will only be executed once in a class, before the first method passed into the fixture function is executed ).

Pass in the fixture function with the scope of class in the function. The fixture function is equivalent to the scope function. See the following example.

import pytest
@pytest.fixture(scope="class")
def get_a():
    a=1
    print('\n执行fixture函数')
    return a

def test000(get_a):
    print('\n执行test000')

def test0000(get_a):
    print('\n执行test0000')

if __name__ == '__main__':
    pytest.main(['-s','test_b.py'])

pytest execution results:

============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 2 items

test_b.py 
执行fixture函数

执行test000
.
执行fixture函数

执行test0000
.

============================== 2 passed in 0.01s ===============================

module

import pytest
@pytest.fixture(scope="module")
def get_a():
    a=1
    print('\n执行fixture函数')
    return a

def test001(get_a):
    print('\n执行test001')
    print("获取a:%d" % get_a)
    assert True

class TestCase:
    def test005(self,get_a):
        print('\n执行test005')
        print("获取a:%d" % get_a)
        assert True

if __name__ == '__main__':
    pytest.main(['-s','test_b.py'])

pytest execution results:

============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 2 items

test_b.py 
执行fixture函数

执行test001
获取a:1
.
执行test005
获取a:1
.

============================== 2 passed in 0.01s ===============================

module: can act on functions and methods in a py file. It is only executed once in a py file, before the first function or method passed into the fixture function is executed.

session

#test_b.py
def test001(get_a):
    print('\n执行test001')

class TestCase:
    def test002(self,get_a):
        print('\n执行test001')
        
#test_c.py
def test003(get_a):
    print('\n执行test003')

class TestCase1:
    def test004(self, get_a):
        print('\n执行test004')
        
#conftest.py
import pytest
@pytest.fixture(scope="session")
def get_a():
    a=1
    print('\n执行fixture函数')
    return a

Results of the:

=========================================================== test session starts ============================================================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 4 items                                                                                                                          

test_b.py 
执行fixture函数

执行test001
.
执行test001
.
test_c.py 
执行fixture函数

执行test003
.
执行test004
.

============================================================ 4 passed in 0.03s ========================================================

session: It is only executed once in a pytest execution. It is executed before the first method or function passed into the fixture function is executed. It needs to be used in conjunction with the conftest.py file and can be executed across py files.

Mixed scope of use

#test_b.py
def test001(get_a):
    print('\n执行test001')

class TestCase:
    def test002(self,get_b):
        print('\n执行test002')
        
#test_c.py
def test003(get_d):
    print('\n执行test003')

class TestCase1:
    def test004(self, get_c):
        print('\n执行test004')
        
#conftest.py
import pytest
@pytest.fixture(scope="session")
def get_a():
    a=1
    print('\n执行fixture函数>>>>>>>>get_a(session)')
    return a

@pytest.fixture(scope="module")
def get_b():
    b=2
    print('\n执行fixture函数>>>>>>>>get_b(module)')
    return b

@pytest.fixture(scope="class")
def get_c():
    c=3
    print('\n执行fixture函数>>>>>>>>get_c(class)')
    return c

@pytest.fixture(scope="function")
def get_d():
    d=4
    print('\n执行fixture函数>>>>>>>>get_d(function)')
    return d

pytest execution results:

=========================================================== test session starts ============================================================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 4 items                                                                                                                          

test_b.py 
执行fixture函数>>>>>>>>get_a(session)

执行test001
.
执行fixture函数>>>>>>>>get_b(module)

执行test002
.
test_c.py 
执行fixture函数>>>>>>>>get_d(function)

执行test003
.
执行fixture函数>>>>>>>>get_c(class)

执行test004
.

============================================================ 4 passed in 0.02s =============================================================

As shown in the example, get_a>test001>get_b>test002>get_d>test003>get_c>test004

Only class-level fixture functions will have function-level effects on functions outside the class. See the following example.

#test_b.py
import pytest
def test001(get_a):
    print('\n执行test001')

def test002(get_a):
    print('\n执行test002')

class TestCase:
    def test003(self,get_a):
        print('\n执行test003')

    def test004(self,get_a):
        print('\n执行test003')

if __name__ == '__main__':
    pytest.main(['-s','test_b.py'])
    
#conftest.py
import pytest
@pytest.fixture(scope="class")
def get_a():
    a=1
    print('\n执行fixture函数>>>>>>>>get_a(class)')
    return a

pytest execution results:

============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 4 items

test_b.py 
执行fixture函数>>>>>>>>get_a(class)

执行test001
.
执行fixture函数>>>>>>>>get_a(class)

执行test002
.
执行fixture函数>>>>>>>>get_a(class)

执行test003
.
执行test003
.

============================== 4 passed in 0.01s ===============================

As shown in the above example, the class-level fixture function get_a is executed before each method outside the class, but the session- and module-level fixture functions do not have this effect.

Calling each other between fixture functions

Fixture functions can call each other.

#test_b.py
import pytest
def test001(get_a):
    print(get_a)
    print('\n执行test001')

def test002(get_a):
    print(get_a)
    print('\n执行test002')

if __name__ == '__main__':
    pytest.main(['-s','test_b.py'])

#conftest.py
import pytest
@pytest.fixture(scope="class")
def get_a(get_b):
    a=get_b+2
    print('\n执行fixture函数>>>>>>>>get_a(class)')
    return a

@pytest.fixture(scope="class")
def get_b():
    b=2
    print('\n执行fixture函数>>>>>>>>get_b(class)')
    return b

pytest execution results:

============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 2 items

test_b.py 
执行fixture函数>>>>>>>>get_b(class)

执行fixture函数>>>>>>>>get_a(class)
4

执行test001
.
执行fixture函数>>>>>>>>get_b(class)

执行fixture函数>>>>>>>>get_a(class)
4

执行test002
.

============================== 2 passed in 0.01s ===============================

Just like the fixture functions get_a and get_b in the example, get_b is passed into get_a as a parameter.

It should be noted that fixture functions calling each other must follow the following rules:

  1. The scope of the fixture function as a parameter must be greater than or equal to the main fixture function. For example, in the above example, the scope of get_b must be larger than get_a, otherwise an error that the fixture function cannot be accessed will occur.
  2. The scope of the fixture function as a parameter is greater than the main fixture function. If the main fixture function is called by case, the fixture function as a parameter still takes effect according to its own scope. See example below.
#test_b.py
import pytest
def test001(get_a):
    print(get_a)
    print('\n执行test001')

def test002(get_a):
    print(get_a)
    print('\n执行test002')

if __name__ == '__main__':
    pytest.main(['-s','test_b.py'])

#conftest.py
import pytest
@pytest.fixture(scope="function")
def get_a(get_b):
    a=get_b+2
    print('\n执行fixture函数>>>>>>>>get_a(function)')
    return a

@pytest.fixture(scope="module")
def get_b():
    b=2
    print('\n执行fixture函数>>>>>>>>get_b(module)')
    return b

pytest execution results:

============================= test session starts ==============================
platform darwin -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: /Users/zy/PycharmProjects/learnpytest
collected 2 items

test_b.py 
执行fixture函数>>>>>>>>get_b(module)

执行fixture函数>>>>>>>>get_a(function)
4

执行test001
.
执行fixture函数>>>>>>>>get_a(function)
4

执行test002
.

============================== 2 passed in 0.01s ===============================

Guess you like

Origin blog.csdn.net/u011090984/article/details/122131018