[pytest] The relationship between tep environment variables, fixtures, and use cases

tep is a testing tool that integrates third-party packages based on the pytest testing framework, provides project scaffolding, and helps to quickly realize the implementation of automated projects by writing Python code.

In the tep project, the automated test cases are placed testsin the directory, and each .pyfile is independent of each other without dependencies. One file is one test case, separated from each other.

Although use cases can also refer to each other, unless it is absolutely necessary, it is generally not recommended to do so. It will lead to the whole body, and it will be difficult to maintain later.

The code writing of the use case is written from top to bottom, which is the same as the conventional writing method of pytest/unittest/script. There will be no learning costs, and generally there will be no problems. Problems with cost may be environment variables and fixtures, because tep is encapsulated and provides a sharing method of dependency injection, and fixture is a knowledge point that is difficult for pytest to understand, so it is necessary to talk about tep environment variables and fixtures through this article 、 The relationship between the three use cases helps to understand, so that the pytest automation project can be realized more flexibly and smoothly with the help of tep.

If you don't use environment variables and fixtures

If you don't use environment variables and fixtures, it's totally possible! For example, create a new script testsunder login_test.py:

from tep.client import request

def test():
    response = request("post",
            url="https://qa.com/api/users/login",
            headers={"Content-Type": "application/json"},
            json={
                "username": "admin",
                "password": "123456",
            }
        )
    assert response.status_code < 400

The request interface https://qa.com/api/users/loginasserts that the response status code is less than 400. Here comes the problem: urlfixed, what should I do if I need to switch between two environments qaand ?release

to parameterize

Whether you are doing automated testing or performance testing, you will come into contact with the word parameterization. It refers to defining the fixed data (hard-coded) in the code as variables, so that the data is different each time it is run, and the fixed data becomes dynamic data. Sources of dynamic data are variables, databases , external files, etc. The type of dynamic data is generally a constant string, or a function, such as JMeter's function assistant, or dependency injection, such as pytest's fixture.

Dependency injected fixtures

"Dependency injection is a technical form of Inversion of Control (IoC, Inversion of Control)", this sentence comes from Wikipedia, I don't know what it means, draw a picture to express it briefly:

It means that if you give clientone injector, clientyou can use it without doing anything service.

pytest's fixture implements dependency injection, allowing us to introduce fixtures to add some extra stuff without modifying the test code.

For urlexample, the domain name needs to be parameterized, and the domain name is different in different environments, so tep makes it a fixture and introduces it through function parameters:

from tep.client import request
from tep.fixture import *

def test(url):  # 引入fixture
    response = request("post",
            url=url("/api/users/login"),
            headers={"Content-Type": "application/json"},
            json={
                "username": "admin",
                "password": "123456",
            }
        )
    assert response.status_code < 400

tep.fixture.urlIt is defined as follows:

@pytest.fixture(scope="session")
def url(env_vars):
    def domain_and_uri(uri):
        if not uri.startswith("/"):
            uri = "/" + uri
        return env_vars.domain + uri

    return domain_and_uri

If you understand it at a glance, congratulations, if you are confused at a glance, it doesn't matter. I will take the time to explain it clearly, it is very important!

Treat fixtures as variables

Although by definition, a fixture is defa function defined with keywords, it can be understood as a variable. for example:

import pytest


@pytest.fixture
def name():
    return "dongfanger"

The usage of the general function is to add parentheses to the function name and pass it name()to get it "dongfanger". The fixture is different, the above definition can be understood as:

name = "dongfanger"

Assign "dongfanger"the value to name, fixture name = return value. nameGet it through variables "dongfanger".

Since it is a variable, you can assign any value, str, function, class, objectany. For example, define a function inside the fixture:

import pytest


@pytest.fixture
def who():
    def get_name():
        return "dongfanger"
    return get_name

It is understood as assigning the function name get_nameto the fixture name variable:

who = get_name

get_nameIs a function name, you need to add parentheses get_name()to get it "dongfanger". whoYou must also pass who()to get it "dongfanger". Let's see tep.fixture.urlif it's clearer:

@pytest.fixture(scope="session")
def url(env_vars):
    def domain_and_uri(uri):
        if not uri.startswith("/"):
            uri = "/" + uri
        return env_vars.domain + uri

    return domain_and_uri

It is understood as assigning the function name domain_and_urito the fixture name variable:

url = domain_and_uri

When using it, url("/api")get the spliced ​​result of the domain name and uri.

Line 2 def url(env_vars):also has a parameter env_vars, and the explanation continues.

The fixture parameter is another fixture

The parameters of the fixture can only be other fixtures. for example:

import pytest


@pytest.fixture
def chinese_name():
    return "东方er"


@pytest.fixture
def english_name(chinese_name):
    return "dongfanger"

Call english_name, pytest will first execute other fixtures in the parameters chinese_name, and then execute itself english_name.

If you tep.fixture.urllook at it in two steps, it will be very clear. The first step:

@pytest.fixture(scope="session")
def url(env_vars):
    func = None
    return func

Step two:

@pytest.fixture(scope="session")
def url(env_vars):
    func = None
    
    
    def domain_and_uri(uri):
        if not uri.startswith("/"):
            uri = "/" + uri
        return env_vars.domain + uri

    
    func = domain_and_uri
    return func

environment variable

tep.fixture.urlThe parameter is another fixture env_varsenvironment variable, which is defined as follows:

from tep.fixture import *


@pytest.fixture(scope="session")
def env_vars(config):
    class Clazz(TepVars):
        env = config["env"]

        """Variables define start"""
        # Environment and variables
        mapping = {
            "qa": {
                "domain": "https://qa.com",
            },
            "release": {
                "domain": "https://release.com",
            }
            # Add your environment and variables
        }
        # Define properties for auto display
        domain = mapping[env]["domain"]
        """Variables define end"""

    return Clazz()

Just look at the middle comment """Variables define start"""to """Variables define end"""the part. urlThe parameterized domain name is here, and mappingthe dictionary establishes the mapping between the environment and variables, and obtains different variable values ​​according to different environment keys.

configThe role of fixture is to read conf.yamlthe configuration in the file.

There are many ways to parameterize. JMeter provides 4 kinds of parameterization ways. The fixture of tep env_varsdraws on the user-defined variables of JMeter:

env_vars.put()and env_vars.get()borrowed from JMeter BeanShell vars.put()and vars.get().

Example: testing multiple URLs

At the end of the talk, an idea was formed. Through practical examples, see how environment variables, fixtures, and use cases are used to deepen the impression. If qathe environment has two websites, the school side and the institution side, both scripts need to be used.

The first modification env_vars, edit fixture_env_vars.py:

        """Variables define start"""
        # Environment and variables
        mapping = {
            "qa": {
                "domain": "https://qa.com",
                "domain_school": "https://school.qa.com",  # 新增
                "domain_org": "https://org.qa.com"  # 新增
            },
            "release": {
                "domain": "https://release.com",
                "domain_school": "https://school.release.com"  # 新增
                "domain_org": "https://org.release.com"  # 新增
            }
            # Add your environment and variables
        }
        # Define properties for auto display
        domain = mapping[env]["domain"]
        domain_school = mapping[env]["domain_school"]  # 新增
        domain_org = mapping[env]["domain_org"]  # 新增
        """Variables define end"""

Added 6 lines of code, defining env_vars.domain_schooland env_vars.domain_org.

The second step is to define fixtures and create new ones fixture_url.py:

@pytest.fixture(scope="session")
def url_school(env_vars):
    def domain_and_uri(uri):
        if not uri.startswith("/"):
            uri = "/" + uri
        return env_vars.domain_school + uri

    return domain_and_uri
    

@pytest.fixture(scope="session")
def url_org(env_vars):
    def domain_and_uri(uri):
        if not uri.startswith("/"):
            uri = "/" + uri
        return env_vars.domain_org + uri

    return domain_and_uri

Refer to tep.fixture.url, modify env_vars.domainto env_vars.domain_schooland env_vars.domain_org, add 2 new fixtures url_schooland url_org.

Going a step further, perhaps defining fixtures login_schooland login_orgflexible options.

summary

This article explains step by step the relationship between tep environment variables, fixtures and use cases, focusing on tep.fixture.urlthe explanation. As long as you understand it, the overall relationship is very clear. The reason why fixtures are used is that multiple people collaborate and share. We need to use functions written by others and reuse return values. Some students are used to defining function parameters. It’s okay if the parameters don’t change. All use cases will report errors. Fixtures limit this very well. It cannot pass parameters by default. Although parameters can be passed by defining internal functions, it is not recommended to do so. I would rather add redundant code and define multiple Fixture is also better than code coupling. The second reason is importthe problem. pytest will automatically find conftest.pythe fixture in it, and tep will further automatically find fixturesthe fixture and import it into it conftest.py. It can importbe used without needing it, reducing importthe code and avoiding the possible circular import problem.

Guess you like

Origin blog.csdn.net/m0_68405758/article/details/130949388