Discussion on developing pytest plug-in based on automated testing framework

In this chapter, we introduce how to develop the pytest plug-in. In the last article, we introduced four python unit testing frameworks. It can be divided into two categories: one must have class inheritance, such as QTAF , and , unittest, and the other can have no class inheritance, such as nose/nose2 , and , pytest. For the framework without class inheritance, the development will be slightly more difficult.

pytest extensibility

If we need to add additional extensibility to pytest, there are three ways.

1. Hook function

Use conf test Py) for this special problem, you can create hook functions.

  • Directory structure:

pytest_sample/
├── conftest.py
└── test_sample.py

In conf test Py file to achieve the following functions.

import pytest

@pytest.fixture
def hello():
    return "hello Entomologist"

Define a function hello() and use pytest The fixture decorator decorates it. The concept of fixture has been introduced earlier. Here, the default level of fixture} is function, which can be understood as that the decorated function will be executed before each function.

Then, in test_sample.py test files to mobilize hook function.

#Call hook function hello
def test_case(hello):
    print("hello:", hello)
    assert hello == "hello Entomologist"

In the test case, the hook function hello() is called as the test case parameter hello. Assert whether the result returned by the Hello function is "Hello bug master"

  • Execution case:

> pytest -vs test_sample.py
================================= test session starts ===========================
collected 1 item

test_sample.py::test_case hello: hello Entomologist
PASSED

================================== 1 passed in 2.61s =============================

2. Case decorator

The commonly used use case decoration is parameter. The usage is as follows.

import pytest


@pytest.mark.parametrize(
    'a, b', 
    [
        (1, 2),
        (2, 3),
        (3, 4),
    ]
)
def test_add(a, b):
    print(f'a:{a}, b:{b}')
    assert a + 1 == b

Use pytest mark. Parameterize() decorator_ Add() test case. Define a and b as parameter variables, and take a set of data for test each time.

  • Operation results

> pytest -vs test_sample.py
================================= test session starts ===========================
collected 3 items

test_sample.py::test_add[1-2] a:1, b:2
PASSED
test_sample.py::test_add[2-3] a:2, b:3
PASSED
test_sample.py::test_add[3-4] a:3, b:4
PASSED

================================== 3 passed in 2.51s =============================

3. Command line parameters

When the use case is executed through the pytest} command, the value is passed as a parameter. Take pytest base URL as an example.

  • install

> pip install pytest-base-url

Write the following use cases:

def test_example(base_url):
    print("base_url:", base_url)
    assert "http" in base_url

The hook function base is also used here_ URL, but base_ The parameter definition of URL is passed in as a parameter when executing the use case.

  • Run test

> pytest -vs test_sample.py --base-url https://www.baidu.com
================================= test session starts ===========================
collected 1 item

test_sample.py::test_example base_url: https://www.baidu.com
PASSED

================================== 1 passed in 2.51s =============================

pytest extension

Implement the pytest Hello plug-in

Let's call this plug-in pytest hello for the time being.

Create pytest_hello.py file, the implementation code is as follows:

import pytest
from typing import Any, Optional


def pytest_configure(config: Any) -> None:
    """
    register an additional marker
    """
    config.addinivalue_line(
        "markers", "env(name): mark test to run only on named environment"
    )


def pytest_runtest_setup(item: Any) -> None:
    """
    Called to perform the setup phase for a test item.
    """
    env_names = [mark.args[0] for mark in item.iter_markers(name="env")]
    if env_names:
        if item.config.getoption("--env") not in env_names:
            pytest.skip("test requires env in {!r}".format(env_names))


@pytest.fixture(scope="function")
def hello(hello_name: str) -> str:
    """
    hello Hook function
    """
    return f"hello, {hello_name}"


@pytest.fixture(scope="function")
def hello_name(pytestconfig: Any) -> Optional[str]:
    """
    hello_name Hook function
    """
    names = pytestconfig.getoption("--hello")
    if len(names) == 0:
        return "Entomologist"
    if len(names) == 1:
        return names[0]
    return names[0]


def pytest_addoption(parser: Any) -> None:
    """
    Add pytest option
    """
    group = parser.getgroup("hello", "Hello")
    group.addoption(
        "--env",
        action="store",
        default=[],
        help="only run tests matching the environment {name}.",
    )
    group.addoption(
        "--hello",
        action="append",
        default=[],
        help="hello {name}",
    )

Main code Description:

  1. In function pytest_ In addoption (), add a command line group hello and two parameters -- env and -- hello.

  2. In the hook function hello_ In name(), get the value of the -- Hello , parameter through pytestconfig , if it is empty, the default value is "bug master". If it is one or more values, take the first one.

  3. In the hook function hello(), get hello_name() and prefixed with "hello.".

  4. In function pytest_ In configure, add the markrs extension env() to get the environment name.

  5. In function pytest_ runtest_ In setup (), obtain the value of env() in markrs and judge whether it is equal to the value of the -- env} parameter. If not, skip the test case, otherwise execute the case.

For the pytest Hello project, you need to create setup Py file. The following chapters will describe how to set this file. Now, refer to the introduction on github and install the pytest Hello plug-in.

github:https://github.com/defnngj/pytest-hello

Pytest Hello use

After you install pytest Hello, use pytest --help to view help information.

> pytest --help
...

Hello:
  --env=ENV             only run tests matching the environment {name}.
  --hello=HELLO         hello {name}
...

In test_sample.py test file, write test cases:

import pytest


@pytest.mark.env("test")
def test_case(hello):
    print("hello:", hello)
    assert "hello" in hello

pytest.mark.env() decorator is an extension of the markrs implementation of pytest hello. Hello is also a hook function implemented by pytest hello.

Execute test cases:

  1. Do not set the -- env parameter or set the parameter value to be non test, and skip the use case.

> pytest -vs test_sample.py --env dev

collected 1 item
test_sample.py::test_case SKIPPED (test requires env in ['test'])
  1. The -- env parameter is set to test, and the -- hello parameter is not set. The default value is "bug master"

> pytest -vs test_sample.py --env test

collected 1 item

test_sample.py::test_case hello: hello, Entomologist
PASSED
  1. Set the -- env parameter value to test and the -- hello parameter value to jack.

Keywords: Python Back-end Programmer software testing PostMan

Added by phast1 on Mon, 24 Jan 2022 16:32:12 +0200