Pytest series - data-driven @pytest.mark.parametrize (7)

Introduction

Comparison of parameterization between unittest and pytest:
An important difference between pytest and unittest is parameterization. The third-party library ddt used by the unittest framework is parameterized.

And the pytest framework:

  • The pre/post-processing function fixture has a parameter params that is specially used in combination with request to pass parameters. Parametrize can also be used in combination with request to pass parameters.
  • For test method parameterization, use decorators directly @pytest.mark.parametrizeto pass parameters to test cases.

Parameterization purpose

  • Parameterization is to extract the data during the test process and pass different data through parameters to drive the use case execution. In fact, it is the concept of data-driven
  • When a test case only has different test data and expected results, but the operation steps are the same, parameterization can be used to improve code reusability and reduce code redundancy.

Examples of practical applications of parameterization

Development scenarios in Web UI automation, such as a login box:

1. It is necessary to test the situation where the account is empty, the password is empty, the account and password are both empty, the account does not exist, the password is wrong, the account and password are correct, etc. 2. The
difference between these use cases lies in the input test data and the corresponding interaction results
3. So we can Write only one login test case, and then parameterize multiple sets of test data and expected results, saving a lot of code

Pytest parameterization

grammar

@pytest.mark.parametrize(args_name,args_values,indirect=False, ids=None,scope=None)

parameter list

  • args_name : parameter name, used to pass parameter values ​​to the function
  • args_values : Parameter values: (list and list dictionary, tuple and dictionary tuple), if there are n values, then the use case is executed n times.
  • indirect : The default is False, which means that parameters are passed in. If set to True, the passed in parameter will be executed as a function instead of a parameter.
  • ids : Customize test id, string list, the length of ids needs to be consistent with the length of the test data list, identify each test case, customize the display of test data results, in order to increase readability
  • scope : If specified, indicates the scope of the parameter.
    Scopes are used to group tests by parameter instances.
    It will also override the scope defined by any fixture function, allowing dynamic scope to be set using the test context or configuration.

Instructions

1. Single parameter

​​@pytest.mark.parametrize()​​The decorator receives two parameters, one parameter is a parameter that identifies the use case function in the form of a string, and the second parameter passes the test data in the form of a list or tuple.

import pytest
#待测试数据
def add(a,b):
    return a+b

# 单个参数的情况
@pytest.mark.parametrize("a",[1,2,3,4])
def test_add(a):  # 作为用例参数,接收装饰器传入的数据
    print(" a的值:",a)
    assert add(a,1) == a+1

Results of the
Insert image description here

2. Multiple parameters

For multiple parameters, ​​@pytest.mark.parametrize()​​the first parameter is still a string. For multiple parameters of the application example, separate them with commas. It is actually an unpacking process.

import pytest

def add(a,b):
    return a+b

@pytest.mark.parametrize("a,b,c",[(1,2,3),(4,5,9),('1','2','12')])
def test_add(a,b,c):
    print(f"\n a,b,c的值:{
      
      a},{
      
      b},{
      
      c}")
    assert add(a,b)==c

Insert image description here
Results of the
Insert image description here

3. Parameterization of the entire test class

The parameterization of the test class actually also parameterizes the test methods in the class.
When a decorator @pytest.mark.parametrizedecorates a test class, it passes the data collection to all test case methods of the class

import pytest

data= [(1,2,3),(4,5,9)]

@pytest.mark.parametrize("a,b,c",data)
class TestStudy:
    def test_parametrize_one(self,a,b,c):
        print(f"\n 测试函数111  测试数据为 {
      
      a}-{
      
      b}")
        assert a+b ==c

    def test_parametrize_two(self, a, b, c):
        print("\n 测试函数222  测试数据为 {}-{}".format(a,b))
        assert a + b == c

Results of the
Insert image description here

4. Superposition and multiplication of multiple parameters

Cartesian product is used when combining multiple parameters .
Cartesian Product:

  • A function or a class can be decorated with multiple @pytest.mark.parametrizes
  • In this way, the final number of use cases generated is n m. For example, the above code is: there are 3 data for parameter a and 2 data for parameter b, so the final number of use cases is 3 2 = 6
  • When there are many parameterized decorators, the number of use cases is equal to n * n * n* n *…
# 笛卡尔积,组合数据
data_1=[1,2,3]
data_2=['a','b']

@pytest.mark.parametrize('a',data_1)
@pytest.mark.parametrize('b',data_2)
def test_parametrize_one(a,b):
    print(f"笛卡尔积 测试数据为:{
      
      a}{
      
      b}")

Parameterize the execution result and pass in the dictionary
Insert image description here

# 字典
data_1=(
    {
    
    
        'user':1,
        'pwd':2
    },
    {
    
    
        'user':3,
        'pwd':4
    }
)

@pytest.mark.parametrize('dic',data_1)
def test_parametrize_1(dic):
    print(f"测数据为 \n {
      
      dic}")
    print(f"user:{
      
      dic['user']},pwd:{
      
      dic['pwd']}")

Execution results
Insert image description here
parameterization, labeled data

# 标记参数化
@pytest.mark.parametrize("test_input,expected",[
    ("3+5",8),
    ("2+4",6),
    pytest.param("6 * 9",42,marks=pytest.mark.xfail),
    pytest.param("6 * 6",42,marks=pytest.mark.skip)
])
def test_parametrize_mark(test_input,expected):
    assert  eval(test_input) == expected

Results of the
Insert image description here

5. ids custom test id

  • ​​@pytest.mark.parametrize()​​​The ids parameter is provided to customize the display results, mainly to see the meaning of the use case more clearly.

  • The length of ids needs to be consistent with the length of the test data list, which is the same as the ids parameter of the fixture. Please refer to the ids of the fixture.

# 增加可读性
data_1=[(1,2,3),(4,5,9)]

ids = ["a:{} + b:{} = expect:{}".format(a,b,expect) for a,b,expect in data_1]

@pytest.mark.parametrize("a,b,expect",data_1,ids=ids)
class TestParametrize(object):
    def test_parametrize_1(self,a,b,expect):
        print(f"测试函数1测试数据为 {
      
      a}--{
      
      b}")
        assert a + b ==expect

    def test_parametrize_2(self,a,b,expect):
        print("测试函数2测试数据为 {}--{}".format(a,b))
        assert  a + b == expect

Results of the
Insert image description here

Request is used in combination with parameterize to pass parameters to Fixture.

  • The params parameters of the fixture itself can be passed in combination with the request. For details, please refer to the introduction chapter of other parameters of the fixture. Of course, you can also use parametrize to parameterize instead of params.

  • indirect=True parameter, the purpose is to execute the incoming data as a function, not as a parameter

  • If the test method is written in a class, the @pytest.mark.parametrizeparameter names of should @pytest.fixturebe consistent with the function names.

Application scenarios:

  • In order to improve reusability, when we write test cases, we will use different fixtures, such as: the most common login operation, the precondition of most use cases is login
  • Assuming that different use cases want to log in to different test accounts, then the account cannot be hard-coded when logging in to the fixture. The login operation needs to be completed by passing parameters.

1. Single parameter

@pytest.fixture()
def test_demo(request):
    name = request.param
    yield name
    print(f"==测试数据是:{
      
      name}==")


data=["ceshi","qianduan"]
ids=[f"test_name is:{
      
      name}" for name in data]

@pytest.mark.parametrize("test_demo",data,ids=ids,indirect=True)
def test_name(test_demo):
    print(f"测试用例的数据是:{
      
      test_demo}")

Execution results
Insert image description here
knowledge points:

  • The indirect=True parameter is added to execute test_demo as a function instead of a parameter and pass data into the function as a parameter.
  • def test_name(test_demo) , test_demo here is the value returned by getting the fixture

2. Multiple parameters

@pytest.fixture()
def logins(request):
    param = request.param
    yield param
    print(f"账号是:{
      
      param['username']},密码是:{
      
      param['pwd']}")

data =[
    {
    
    "username":"张三","pwd":"123456"},
    {
    
    "username":"李四","pwd":"12345"}
]

@pytest.mark.parametrize("logins",data,indirect=True)
def test_name_pwd(logins):
    print(f"账号是:{
      
      logins['username']},密码是:{
      
      logins['pwd']}")

Execution results
Insert image description here
If multiple parameters need to be passed, they need to be passed through a dictionary.

3. Multiple fixtures (only add one decorator) and multiple parametrizes (overlay decorators)

# 多个fixture
@pytest.fixture()
def login_user(request):
    user = request.param
    yield user
    print("账号:%s" % user)

@pytest.fixture()
def login_pwd(request):
    pwd = request.param
    yield pwd
    print("密码:%s" % pwd)

data =[
    {
    
    "username":"张三","pwd":"123456"},
    {
    
    "username":"李四","pwd":"12345"}
]

@pytest.mark.parametrize("login_user,login_pwd",data,indirect=True)
def test_more_fixture(login_user,login_pwd):
    print("fixture返回的内容:",login_user,login_pwd)

Results of the
Insert image description here

@pytest.fixture(scope="function")
def login_user(request):
    user = request.param
    yield user
    print("账号:%s" % user)

@pytest.fixture(scope="function")
def login_pwd(request):
    pwd = request.param
    yield pwd
    print("密码:%s" % pwd)

name= ["张三","李四"]
pwd = ["123456","12345"]

@pytest.mark.parametrize("login_user",name,indirect=True)
@pytest.mark.parametrize("login_pwd",pwd,indirect=True)
def test_more_fixture(login_user,login_pwd):
    print("fixture返回的内容:",login_user,login_pwd)

Results of the
Insert image description here

[Reference Article] Little Pineapple Test Notes

Guess you like

Origin blog.csdn.net/m0_62091240/article/details/132839957