fixture for advanced usage of pytest in Python testing framework

Keywords: Python pytest

Sun cool College: https://shareku.ke.qq.com/
           
Micro signal: Please specify shaiku College for addition         QQ group: 979438600
       

Preconditions:
1. File path:

Test_App
    - - test_abc.py
    - - pytest.ini

2. Content of pyetst.ini configuration file:

 [pytest]
  Command line parameters
 addopts = -s
 Search file name
 python_files = test*.py
  Class name to search
 python_classes = Test*
Function name to search
 python_functions = test_*

fixture of pytest
The fixture modifier marks a fixed factory function. It will be activated and executed preferentially when called by other functions, modules, classes or the whole project. It is usually used to complete preset processing and repeated operations.

method: fixture(scope="function", params=None, autouse=False, ids=None, name=None)
Common parameters:
scope: Scope of marked method  session>module>class>function
function" (default): For each test method, each test All run once
"class": Acts on the entire class, each class All of test Run only once. There can be multiple methods in a class
"module": Act on the whole module, each module All of test Only run once; every last.py The file is called once, and there are multiple files in the file function and class
"session: Act on the whole session(Use with caution),each session Only run once; Multiple files can be called once, which can span multiple files.py File call, each.py File is module
params: (list type)Provide parameter data for the function calling the marked method; An optional parameter list that will cause multiple parameter calls fixture Function and all tests use it.
autouse: Whether to run automatically,Default to False Do not run, set to True Automatic operation; If True,Is active for all tests fixture func You can see it. If yes False The display requires a reference to activate fixture<br><br>ids: Each string id Each string corresponds to params So they are testing ID Part of. If not provided ID They will be from params Automatic generation;It's for each item params Parameter settings are used for user-defined names, which is of little significance.

Name: the name of the fixture. This defaults to the name of the decorator function. If the first mock exam is used in the unified module of fixture, the function name of the fixture will be obscured by the function of the request fixture. arg will be decorated with the function function to "fixture_" and then use @pytest.fixture "name=".

First example of fixture (by parameter reference)

class Test_ABC:
    @pytest.fixture()
    def before(self):
        print("------->before")
    def test_a(self,before): # ️ test_ The a method passes in the function identified by the fixture, which is in the form of a variable
        print("------->test_a")
        assert 1
if __name__ == '__main__':
    pytest.main(["-s  test_abc.py"])


Use multiple fixture s
If the use case needs to use the returned data of multiple fixtures, fixtures can also return a primitive ancestor, list or dictionary, and then take the corresponding data from it.

import pytest
@pytest.fixture()
def test1():
    a = 'door'
    b = '123456'
    print('Outgoing a,b')
    return (a, b)
def test_2(test1):
    u = test1[0]
    p = test1[1]
    assert u == 'door'
    assert p == '123456'
    print('The form of Yuanzu is correct')
if __name__ == '__main__':
    pytest.main(['-s','test_abc.py'])


Of course, it can also be divided into multiple fixtures, and then multiple fixture parameters can be passed in the use case

import pytest
@pytest.fixture()
def test1():
    a = 'door'
    print('\n Outgoing a')
    return a
@pytest.fixture()
def test2():
    b = '123456'
    print('Outgoing b')
    return b
def test_3(test1, test2):
    u = test1
    p = test2
    assert u == 'door'
    assert p == '123456'
    print('Incoming multiple fixture The parameters are correct')
if __name__ == '__main__':
    pytest.main(['-s','test_abc.py'])


Second example of fixture (through function reference)

import pytest
@pytest.fixture() # The function marked fixture can be applied outside the test class
def before():
    print("------->before")
@pytest.mark.usefixtures("before")#1. The before function needs to be marked before it can be used, so the before function needs to be marked before @ pytest.fixture();2. The before function is marked in the front. If it is not referenced, the before function will not be executed after execution
                     For example, if you need to log in first in the interface test, you can use this usage<br>class Test_ABC:
    def setup(self):
        print("------->setup")
    def test_a(self):
        print("------->test_a")
        assert 1
if __name__ == '__main__':
          pytest.main(["-s","test_abc.py"])


Use the decorator @ pytest.mark.usefixtures() to decorate the use cases that need to be run

import pytest
@pytest.fixture()
def test1():
    print('\n Start execution function')
@pytest.mark.usefixtures('test1')
def test_a():
    print('---Use case a implement---')
@pytest.mark.usefixtures('test1')
class Test_Case:
    def test_b(self):
        print('---Use case b implement---')
    def test_c(self):
        print('---Use case c implement---')
if __name__ == '__main__':
    pytest.main(['-s', 'test_abc.py'])


Superimpose usefixtures
If a method or a class use case wants to call multiple fixtures at the same time, you can use @ pytest.mark.usefixture() for superposition. Pay attention to the stacking sequence, and put the bottom layer first and then the upper layer.

import pytest
@pytest.fixture()
def test1():
    print('\n Start execution function1')
@pytest.fixture()
def test2():
    print('\n Start execution function2')
@pytest.mark.usefixtures('test1')
@pytest.mark.usefixtures('test2')
def test_a():
    print('---Use case a implement---')
@pytest.mark.usefixtures('test2')
@pytest.mark.usefixtures('test1')
class Test_Case:
    def test_b(self):
        print('---Use case b implement---')
    def test_c(self):
        print('---Use case c implement---')
if __name__ == '__main__':
    pytest.main(['-s', 'test_abc.py'])


Difference between using fixtures and passing fixtures
If the fixture has a return value, the usefixture cannot obtain the return value. This is the difference between the decorator usefixture and the fixture parameter directly passed by the use case.
When a fixture needs to use the parameters from return, you can only use the parameter name. When the parameters are passed in directly, you don't need to use the parameters from return. Both methods can be used.

The third example of fixture (the default setting is run, and the scope is function)

import pytest
@pytest.fixture(scope='function',autouse=True) # Set the scope to function and run automatically
def before():
    print("------->before")
class Test_ABC:
    def setup(self):
        print("------->setup")
    def test_a(self):
        print("------->test_a")
        assert 1
    def test_b(self):
        print("------->test_b")
        assert 1
if __name__ == '__main__':
        pytest.main(["-s", "test_abc.py"])


Fifth example of fixture (set the scope to class)

import pytest
@pytest.fixture(scope='class',autouse=True) # Set the scope to class and run automatically
def before():
    print("------->before")
class Test_ABC:
    def setup(self):
        print("------->setup")
    def test_a(self):
        print("------->test_a")
        assert 1
    def test_b(self):
        print("------->test_b")
        assert 1
if __name__ == '__main__':
    pytest.main(["-s","test_abc.py"])


Sixth example of fixture (return value)

import pytest
@pytest.fixture()
def need_data():
    return 2  # Returns the number 2
class Test_ABC:
    def test_a(self, need_data):
        print("------->test_a")
        assert need_data != 3  # Get the return value and make an assertion
if __name__ == '__main__':
    pytest.main(["-s",  "test_abc.py"])

import pytest
@pytest.fixture(params=[1, 2, 3])
def need_data(request):  # Pass in parameter request system encapsulation parameter
    return request.param  # Take a single value in the list. The default value method is
class Test_ABC:
    def test_a(self, need_data):
        print("------->test_a")
        assert need_data != 3  # Assert need_data is not equal to 3
if __name__ == '__main__':
    pytest.main(["-s","test_abc.py"])

The results were found to have been run three times

Fixture (set the scope to module)

import pytest
@pytest.fixture(scope='module')
def test1():
    b = 'male'
    print('It came out%s, And in the current py Execute the file next time!!!' % b)
    return b
def test_3(test1):
    name = 'male'
    print('find name')
    assert test1 == name
class Test_Case:
    def test_4(self, test1):
        sex = 'male'
        print('find sex')
        assert test1 == sex
if __name__ == '__main__':
    pytest.main(["-s","test_abc.py"])


Fixture (set the scope to session)

When fixture is session level, it can be called across. Py modules. That is, when we have multiple use cases with. Py files, if multiple use cases only need to call fixture once, it can be set to scope = "session" and written to the confitest.py file.

The name of the conftest.py file is fixed, and pytest will automatically recognize the file. Put it in the root directory of the project and it can be called globally. If it is placed in a package, it will be valid in the changed package.

import pytest
# conftest.py
 
@pytest.fixture(scope='session')
def test1():
    a='door'
    print('Get%s' % a)
    return a
import pytest
# abc.py
 
def test3(test1):
    b = 'pen'
    print('find b')
    assert test1 == b
 
 
if __name__ == '__main__':
    pytest.main(['-s', 'abc.py'])
import pytest
# abc1.py
 
class Test_Case:
 
    def test_4(self, test1):
        a = 'door'
        print('find a')
        assert test1 == a
 
if __name__ == '__main__':
    pytest.main(['-s', 'abc1.py'])

If you need to execute two py files at the same time, you can execute the command in cmd under the directory where the PY file is located: pytest -s test_abc.py test_abc.py
 
Scope of conf test.py

Multiple files of confitest.py can be created in a project. Generally, the confitest file set in the project root directory plays a global role. The file confitest.py can also be placed in different subdirectories. The scope of action can only take effect in the changed level and the following directories.
1. The scope of confitest is different at different levels
2. Conf test cannot be called across modules (module calls are not used here)

fixture auto use autouse=True

When there are many use cases, it is troublesome to pass this parameter every time. There is a parameter autouse in the fixture, which is False by default. If it is not enabled, it can be set to True to enable the automatic use of the fixture function, so that the use case does not have to pass parameters every time

Usually writing automation examples will write some front-end examples fixture Operation. If the use case needs to be used, just pass the parameter name of the function directly. When there are many use cases, it is troublesome to pass this parameter every time.
fixture There is a parameter in it autouse,The default is Fasle If it is not turned on, it can be set to True Turn on automatic use fixture Function, so that the use case does not have to pass parameters every time
 set up autouse=True
autouse Set to True,Automatic call fixture function
start set up scope by module Level, in current.py The use case module is executed only once, autouse=True Automatic use[picture]open_home set up scope by function Level,
Each use case is called once before and used automatically

Set autouse to True to call the fixture function automatically

import pytest
@pytest.fixture(scope='module', autouse=True)
def test1():
    print('\n Start execution module')
@pytest.fixture(scope='class', autouse=True)
def test2():
    print('\n Start execution class')
@pytest.fixture(scope='function', autouse=True)
def test3():
    print('\n Start execution function')
def test_a():
    print('---Use case a implement---')
def test_d():
    print('---Use case d implement---')
class Test_Case:
    def test_b(self):
        print('---Use case b implement---')
    def test_c(self):
        print('---Use case c implement---')
if __name__ == '__main__':
    pytest.main(['-s', 'test_abc.py'])


As a result, you can see that scope=class also acts on functions outside class

params usage example of fixture
request is the built-in fixture of pytest, which is mainly used to pass parameters

The params parameter of the Fixture parameter implements parameterization: (it can be list and tuple, or dictionary list, dictionary ancestor, etc.)

import pytest
  
def read_yaml():
    return ['1','2','3']
  
@pytest.fixture(params=read_yaml())
def get_param(request):
    return request.param
  
def test_01(get_param):
    print('Test case:'+get_param)
  
if __name__ == '__main__':
    pytest.main(['-s','test_abc.py'])<br>Execution results:

============================= test session starts =============================
platform win32 – Python 3.5.2, pytest-6.0.2, py-1.9.0, pluggy-0.13.1
rootdir: E:\PycharmProjects\lianxi, configfile: pytest.ini
plugins: forked-1.3.0, html-1.22.1, metadata-1.8.0, rerunfailures-9.1, xdist-2.1.0
collected 3 items

test_abc.py test case: 1
. test case: 2
Test case: 3
.

------ generated html file: file://E:\PycharmProjects\lianxi\report.html ------
============================== 3 passed in 0.13s ==============================

be careful:

1. In this example, the test01 method is executed three times, and the data used are '1', '2' and '3', which is similar to the ddt data-driven function. Special note: the request parameter name here is fixed, and the param of request.param has no s.

2. You can change return request.param to yield request.param. Yield also means return. The difference between yield and return is that return cannot be followed by a code, but yield can be followed by a code.

Posted by dinosoup on Sun, 05 Dec 2021 13:22:03 -0800