pytest

Mature, full-featured Python testing framework

  • Simple and flexible, easy to use
  • Support for parameterization
  • Skip and Xfail test cases, automatic failure retry, etc
  • Support simple unit tests and complex functional tests. It can also be used for Selenium/APPIum automation tests and PyTest + Requests.
  • There are many third-party plug-ins and you can customize extensions: Pytest-Allure, Pytest-Xdist (multi-CPU development), etc
  • Support Jenkins integration

Pytest User manual

Pytest library installation:

pip install pytest 

Test case identification and execution

  1. Naming requirements for files:
    • test_*.py
    • *_test.py
  1. Naming requirements for use cases:
    • The Test* class contains all test_* methods (Test classes cannot have _init_Methods)
    • All test_* methods that are not in class

Pytest can implement use cases and methods from the UnitTest framework

Pycharm to use the PyTest framework, you need to specify it as shown in the figure below:

Command line operation mode:

Pytest # Execute the specified file pytest test_material_master.py # Execute the log pytest -vCopy the code

Restore the way you run with the Python interpreter:

The running mode is as follows:

import pytest if __name__ == '__main__': Pytest.main (["test_material_master.py"]) # Run the entire file use case Pytest. Main ([" test_material_master. Py: : TestMaterialMaster: : test_mat_search_by_matCode "]) # run file specified for a use caseCopy the code

Parameterized use

Usage:

Pytest.mark.parametrize (argnames, argvalues) # argNames: parameterized variable; Types: STR (comma separated),list,tuple # argvalues: parameterized values; Type: list, (a tuple)Copy the code

Practical application:

Pytest.mark. parametrize("a, b, expect", get_data(yaml_path, "add"), ids=[" integer ", "decimal "," big integer "]) def test_add(self, a, b, Expect, setup_fixture): """ test positive use case of addition "" result = setup_fixture. Add_func (a, b) assert abs(result-expect) < 0.01Copy the code

Matters needing attention:

Parameter combination (Cartesian product) : Applies when there is only one desired result

import pytest @pytest.mark.parametrize("a", [1, 2, 3]) @pytest.mark.parametrize("b", [4, 5, 6]) def test_add(a, b): Print (" parameter combination a = {}, b = {}". Format (a, b)) print(" parameter combination a = {}, b = {}". PASSED [11%] Parameter combination A = 1, B = 4 PASSED [22%] Parameter combination A = 2, B = 4 PASSED [33%] Parameter combination A = 3, B = 4 PASSED [44%] Parameter combination A = 1, B = 5 PASSED [55%] Parameter combination A = 2, B = 5 PASSED [66%] parameter combination A = 3, B = 5 PASSED [77%] parameter combination A = 1, B = 6 PASSED [88%] Parameter combination A = 2, B = 6 PASSED [100%] Parameter combination A = 3, b = 6Copy the code

skip

Usage scenario: When writing test cases, you can skip a use case that has a bug and cannot be fixed for the moment

Import [email protected] (" There is a bug, Parametrize ("a", [1, 2, 3]) @pytest.mark.parametrize("b", [4, 5, 6]) def test_add(a, b): Print (" a = {}, b = {}". Format (a, b))Copy the code

mark

Usage scenario: Classify and label use cases

Import [email protected] def test_01(): print @pytest.mark.test2 def test_02(): import [email protected] def test_01(): print @pytest.mark.test2 def test_02(): Pytest -s test.py -m test1 # pytest -s test.py -m "not test1" # pytest -s test.py -m "not test1"Copy the code

Front and back

Use case run level

  • Module level: start and end of module, globally valid;setup_module / teardown_module 
  • Function-level: applies only to function use cases (not to classes);setup_function / teardown_function 
  • Class level: run only once before and after a class (used in a class);setup_class / teardown_class 
  • Method level: start and end of method (used in classes);setup_method / teardown_method 
  • Class, run before and after a method is called;setup / teardown  The most commonly used* * * *

Fixture: Custom pre-and-post-fixture

Advantages of @pytest.fixture() :

  • Naming is flexible, not limited to setup/teardown
  • The conftest.py configuration enables data sharing and automatic identification without import
  • scope="module"Multiple.py cross-file sharing prefixes can be implemented, with each.py file called once
  • scope="session"Multiple.py can be implemented across files using a single session to complete multiple use cases
Import pytest@pytest. fixture(scope="function") # def login_fixture(): Def test_01(login_fixture): # login_fixture (test case 01) def test_02(): Py ::test_01 PASSED in advance [50%] test case 01 test.py::test_02 PASSED [100%] Test case 02Copy the code

Fixture parameters

Scope: scope

  • Function: Each test runs, defaulting to scope for function
  • Class: All tests for each class are run only once
  • Module: All tests for each module are run only once
  • Session: Each session runs only once

Autouse: Use with caution

  • The default is False
  • When True, each test case automatically invokes the fixture without passing in the fixture function name

Arguments are used in fixtures

Fixture functions can be parameterized, in which case they will be called multiple times to execute a set of related tests, the tests that depend on the fixture, and the test functions usually don’t need to know about their rerun

Import pytest@pytest. fixture(params=[" params "," params "]) def myfixture(request): pytest@pytest. fixture(params=[" params "," params "]) def myfixture(request): Print (" execute function in testPytest, %s" % request.param)Copy the code

Arguments are returned in fixtures

Import pytest@pytest. fixture(params=[" params "," params "]) def myfixture(request): pytest@pytest. fixture(params=[" params "," params "]) def myfixture(request): return request.param def test_print_param(myfixture): Print (" execute test_two") print(myfixture) Assert 1==1 # Output PASSED [50%] Execute test_two parameter 1 PASSED [100%] execute test_two parameter 2Copy the code

conftest.py

Application Scenarios:

  • Token shared by each interface
  • Test case data shared by each interface
  • Configuration information shared by each interface

Precautions for use:

  • The file name conftest.py is fixed and cannot be changed to another file name
  • The conftest.py file must be under the same pakage as the running use case and haveinitThe lead-in file
  • Instead of importing through import, pyTest’s use case recognizes it automatically
  • The conftest.py file is executed before all test files in the same directory are run
Import pytest@pytest. fixture() def login(): printCopy the code

Post-operation -yield

@pytest.fixture(scope="function") def demo_fix(): print() yield print() def test_1(demo_fix): pytest.fixture(scope="function") def demo_fix(): Def test_2(): def test_3(demo_fix): print() def test_3(demo_fix): print() def test_3(demo_fix): print() def test_3(demo_fix): print()Copy the code

If the code in the test case is abnormal or the assertion fails, it will not affect the code execution after yield in his firmware;

But if there is an error or assertion failure in the pre-yield code in the fixture, which is the equivalent of the setup part, the post-yield code will not execute, and certainly the test case code will not execute

The final function – AddFinalizer

Equivalent to try… Except in the finally

Even if setup fails, AddFinalizer will still perform teardown

@pytest.fixture(scope="session") def login_xadmin_fix(request): s = requests.session() login_xadmin(s) def close_s(): S.close () # Close final cleanup request. Addfinalizer (close_s) return s after the use case is completeCopy the code

Common Parameters

  • -v: displays detailed information about the execution of a use case. The file where the use case sits and the name of the use case
  • -s: Displays debugging information about a use case. Print information, such as print
  • -x: Stops immediately when a use case fails
  • -maxfail: stops running when a certain number of use cases fail;pytest -maxfail=num 
  • -m: Indicates that the command is running@ pytest. Mark. The tag nameTest cases for
  • -k: performs matching test cases. Such aspytest -k "raises and not delete"Run all tests that contain RAISES but do not contain DELETES

Introduction to pyTest utility plug-in

Pytest-rerunfailures: Automatically rerun the use case when it fails

Installation method:

pip install pytest-rerunfailures
Copy the code

Usage:

Pytest test_x.py --reruns=n # Number of times to run after a failureCopy the code

You can also specify the number of times to reruns in the script, so that you do not need to use the –reruns parameter when running

@pytest.mark.flaky(reruns=6, reruns_delay=2)
def test_example(self):
    print(3)
    assert random.choice([True, False])
Copy the code

Pytest-assume: double check

Python’s Assert assertion in PyTest can also write multiple assertions, but if one fails, subsequent assertions will no longer be executed

Pytest-assume, on the other hand, will assume even if the previous assertion fails

Installation method:

pip install pytest-assume
Copy the code

Usage:

def test_simple_assume(x, y):
    pytest.assume(x == y)
    pytest.assume(True)
    pytest.assume(False)
Copy the code

Pytest-xdist: Distributed and concurrent execution

Pytest-xdist allows automated test cases to be distributed, saving automated testing time

Design principles for distributed execution use cases:

  1. Use cases are independent of each other. There is no dependency between use cases. Use cases can run completely independently.
  2. Use case execution is not sequential, random order will be normal execution
  3. Each use case can be run repeatedly without affecting other use cases.

Installation method:

pip install pytest-xdist
Copy the code

Usage:

In the case of parallel execution of multiple cpus, add the -n parameter, num parameter is the number of parallel execution, for example, set num to 3

pytest -n 3
Copy the code

Pytest-ordering: Controls the execution order of use cases

Installation method:

pip install pytest-ordering
Copy the code

Usage:

import pytest

@pytest.mark.run(order=2)
def test_foo():
    assert True

@pytest.mark.run(order=1)
def test_bar():
    assert True
Copy the code

PS: Try not to make test cases sequential, try not to make test cases dependent!

Hook function customization and extension plug-ins

Pytest calls this hook method after collecting all the test cases. We can customize the function implementation:

  1. Customize the order of use case execution
  2. Solve coding problem (Chinese test case name)
  3. Automatic label adding

It is recommended to write the code for hook functions in the conftest.py file

# conftest.py
from typing import List


def pytest_collection_modifyitems(
    session: "Session", config: "Config", items: List["Item"]
) -> None:
    for item in items:
        item.name = item.name.encode("utf-8").decode("unicode-escape")
        item._nodeid = item.nodeid.encode("utf-8").decode("unicode-escape")
Copy the code