It took 2 weeks to sort it out: "pytest test test framework" nanny tutorial (test beginners must see!)

Hello, I'm erhei. Here's a free copy of software test data

  • 1. Software testing learning route
  • 2. Software test video data
  • 3. Software test related documents
  • 4. Software testing related tools and installation packages
  • 5. Resume template of Senior Test Engineer
  • 6. Interview questions, mock interview, PDF documents

Small partners need to be concerned about my official account: buddies are two black, free to receive.

install

pip install pytest

brief introduction

pytest can easily write tests, support extensions, and have rich references and libraries to support complex function tests

A simple example:

# content of test_sample.py
def inc(x):
    return x + 1


def test_answer():
    assert inc(3) == 5

results of enforcement

$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 1 item

test_sample.py F                                                     [100%]

================================= FAILURES =================================
_______________________________ test_answer ________________________________

    def test_answer():
>       assert inc(3) == 5
E       assert 4 == 5
E        +  where 4 = inc(3)

test_sample.py:6: AssertionError
========================= 1 failed in 0.12 seconds =========================

Functional features

  • The failed statement has detailed information (no need to remember self.assert* names);

  • Automatically discover the modules and methods of testing (through the naming method of modules and functions);

  • Use fixtures to manage test resources;

  • It can be compatible with unittest and nose test components;

  • Python 2.7, Python 3.4+, PyPy 2.3, Jython 2.5 (untested);

  • Rich plug-in resources, more than 315 external plug-ins and booming communities;

  • Support parameterization

  • During test execution, some tests can be skipped or some case s that are expected to fail can be marked as failed

  • Support repeated failed case s

fixtures

fixtures provides a fixed baseline for reliably repeating tests. pytest fixture provides significant improvements over the setup/teardown function of the classic xUnit:

  • fixtures have explicit names and are activated by declaring their use from test functions, modules, classes, or the entire project.

  • Fixtures is implemented in a modular manner because each fixture name triggers the fixtures method, which itself can use other fixtures.

  • Fixtures management extends from simple units to complex functional tests, allowing parameterization of fixtures and tests according to configuration and component options, or reuse fixtures within functions, classes, modules or the entire test session.

In addition, pytes also supports the classic xunit style. You can mix the two styles as needed, and gradually move from the classic style to the new style. You can also start from an existing unittest Start with a testcase style or nose based project.

Expand - xunit style:

  • setUp/tearDown;

  • setUpClass/tearDownClass;

  • setUpModule/tearDownModule;

Expand scope:

  • The module level (setup_module/teardown_module) starts from the beginning to the end of the module and is global

  • Function level (setup_function/teardown_function) is only valid for function use cases (not in classes)

  • The class level (setup_class/teardown_class) runs only once (in a class) before and after the class

  • The method level (setup_method/teardown_method) starts at the beginning and end of the method (in the class)

  • Class (setup/teardown) runs before and after calling the method

fixtures as function parameters

Let's look at a simple stand-alone test module that contains a fixture and a test function that uses it

# content of ./test_smtpsimple.py
import pytest

@pytest.fixture
def smtp_connection():
    import smtplib
    return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)

def test_ehlo(smtp_connection):
    response, msg = smtp_connection.ehlo()
    assert response == 250
    assert 0 # for demo purposes

Here, test_ehlo requires SMTP_ Return value of connectio. Pytest finds and calls @ pytest Fixture tagged smtp_connection fixture function. Run the test as follows:

$ pytest test_smtpsimple.py
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 1 item

test_smtpsimple.py F                                                 [100%]

================================= FAILURES =================================
________________________________ test_ehlo _________________________________

smtp_connection = <smtplib.SMTP object at 0xdeadbeef>

    def test_ehlo(smtp_connection):
        response, msg = smtp_connection.ehlo()
        assert response == 250
>       assert 0 # for demo purposes
E       assert 0

test_smtpsimple.py:11: AssertionError
========================= 1 failed in 0.12 seconds =========================

The procedure of calling pytest is as follows:

  • pytest found test_ehlo function, because test_ Prefix. test_ehlo needs a file named SMTP_ Function parameter of connection. Find the matching SMTP by looking up a function named tag fixture_ Connection function

  • smtp_connection() is called to create an instance

  • test_ Ehlo (< smtp_connection instance >) is called

Shared fixture function

If you realize that you want to use the fixture function in multiple test files during the implementation of the test, you can move it to confitest Py file. You do not need to import the fixture to be used in the test, it will be found by pytest automatically.

The following example puts the fixture function into a separate confist. Exe Py file so that tests from multiple test modules in the directory can access the fixture function

# content of conftest.py
import pytest
import smtplib

@pytest.fixture(scope="module")
def smtp_connection():
    return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)

The name of this fixture is smtp_connection, which can be accessed by listing the name as an input parameter in any test file (in the directory where conf test.py is located):

# content of test_module.py

def test_ehlo(smtp_connection):
    response, msg = smtp_connection.ehlo()
    assert response == 250
    assert b"smtp.gmail.com" in msg
    assert 0  # for demo purposes

def test_noop(smtp_connection):
    response, msg = smtp_connection.noop()
    assert response == 250
    assert 0  # for demo purposes

fixture scope

@pytest.fixture(scope = "module") uses scope to control the scope, function, class, module, package and session of fixture.

teardown code

xunit style code setup and teardown appear in pairs. In addition to being compatible with this mode, pytest also supports the execution of fixture specific termination code. By using the yield statement instead of return, all code after the yield statement is used as teardown Code:

# content of conftest.py

import smtplib
import pytest


@pytest.fixture(scope="module")
def smtp_connection():
    smtp_connection = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
    yield smtp_connection  # provide the fixture value
    print("teardown smtp")
    smtp_connection.close()

When the last test in the module has been completed, the statement SMTP regardless of the test situation_ connection. Close() will be executed

Let's execute:

$ pytest -s -q --tb=no
FFteardown smtp

2 failed in 0.12 seconds

Please note that if we use the scope='function 'fixture to set the method of fixture decoration, each individual test will be cleaned

We can also use the yield syntax with statements

# content of test_yield2.py

import smtplib
import pytest


@pytest.fixture(scope="module")
def smtp_connection():
    with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp_connection:
        yield smtp_connection  # provide the fixture value

The yield keyword is used in the python syntax generator to save memory

Parameterization

fixture parameterization

When the fixture method is called multiple times and performs the same set of tests each time, in this case, the fixture method can be parameterized.

Extending the previous example, we can create two SMPS by marking fixture_ Connection instance. The fixture function accesses each parameter through the request object:

# content of conftest.py
import pytest
import smtplib

@pytest.fixture(scope="module",
                params=["smtp.gmail.com", "mail.python.org"])
def smtp_connection(request):
    smtp_connection = smtplib.SMTP(request.param, 587, timeout=5)
    yield smtp_connection
    print("finalizing %s" % smtp_connection)
    smtp_connection.close()

Run test:

$ pytest -q test_module.py
FFFF                                                                 [100%]
================================= FAILURES =================================
________________________ test_ehlo[smtp.gmail.com] _________________________

smtp_connection = <smtplib.SMTP object at 0xdeadbeef>

    def test_ehlo(smtp_connection):
        response, msg = smtp_connection.ehlo()
        assert response == 250
        assert b"smtp.gmail.com" in msg
>       assert 0  # for demo purposes
E       assert 0

test_module.py:6: AssertionError
________________________ test_noop[smtp.gmail.com] _________________________

smtp_connection = <smtplib.SMTP object at 0xdeadbeef>

    def test_noop(smtp_connection):
        response, msg = smtp_connection.noop()
        assert response == 250
>       assert 0  # for demo purposes
E       assert 0

test_module.py:11: AssertionError
________________________ test_ehlo[mail.python.org] ________________________

smtp_connection = <smtplib.SMTP object at 0xdeadbeef>

    def test_ehlo(smtp_connection):
        response, msg = smtp_connection.ehlo()
        assert response == 250
>       assert b"smtp.gmail.com" in msg
E       AssertionError: assert b'smtp.gmail.com' in b'mail.python.org\nPIPELINING\nSIZE 51200000\nETRN\nSTARTTLS\nAUTH DIGEST-MD5 NTLM CRAM-MD5\nENHANCEDSTATUSCODES\n8BITMIME\nDSN\nSMTPUTF8\nCHUNKING'

test_module.py:5: AssertionError
-------------------------- Captured stdout setup ---------------------------
finalizing <smtplib.SMTP object at 0xdeadbeef>
________________________ test_noop[mail.python.org] ________________________

smtp_connection = <smtplib.SMTP object at 0xdeadbeef>

    def test_noop(smtp_connection):
        response, msg = smtp_connection.noop()
        assert response == 250
>       assert 0  # for demo purposes
E       assert 0

test_module.py:11: AssertionError
------------------------- Captured stdout teardown -------------------------
finalizing <smtplib.SMTP object at 0xdeadbeef>
4 failed in 0.12 seconds

We see two test functions for different SMTP_ The connection instance was run twice

Test function parameters

Built in pytest mark. The parameterize decorator supports parameterization of parameters of test functions. The following is a typical example of a test function that checks whether an input causes the expected output:

# content of test_expectation.py
import pytest


@pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
def test_eval(test_input, expected):
    assert eval(test_input) == expected

Run them three times:

$ pytest
=========================== test session starts ============================
platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y
cachedir: $PYTHON_PREFIX/.pytest_cache
rootdir: $REGENDOC_TMPDIR
collected 3 items

test_expectation.py ..F                                              [100%]

================================= FAILURES =================================
____________________________ test_eval[6*9-42] _____________________________

test_input = '6*9', expected = 42

    @pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)])
    def test_eval(test_input, expected):
>       assert eval(test_input) == expected
E       AssertionError: assert 54 == 42
E        +  where 54 = eval('6*9')

test_expectation.py:6: AssertionError
==================== 1 failed, 2 passed in 0.12 seconds ====================

How to call fixture

# content of conftest.py
import pytest
import smtplib

@pytest.fixture(scope="module")
def smtp_connection():
    return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)

In the method, you can directly use the function name identified by fixture to call:

# content of test_module.py

def test_ehlo(smtp_connection):
    response, msg = smtp_connection.ehlo()
    assert response == 250
    assert b"smtp.gmail.com" in msg
    assert 0  # for demo purposes

You can also use the declaration decorator @ pytest mark. Usefixtures call

# content of test_module.py

@pytest.mark.usefixtures("smtp_connection")
def test_ehlo():
    response, msg = smtp_connection.ehlo()
    assert response == 250
    assert b"smtp.gmail.com" in msg
    assert 0  # for demo purposes

When decorator @ pytest mark. When usefixtures acts on a class, if this @ pytest scope=function of the fixture, then each test method in the class will call the fixture.

@pytest.fixture(scope = "module", autouse=True), parameter autouse, the default setting is False. When the default is False, you can choose to use the above two methods to try the fixture. When set to True, all tests in a session will automatically call this fixture. So be careful when using this function

pytest common plug-ins

pip install pytest-html #Lightweight test report
pytest 'file' --html=report.html
pip install pytest-sugar # Print progress
pip install pytest-rerunfailures # Failed to retry
pip install pytest-ordering # Execution sequence

Upgraded version of test report - allure

pip install pytest-allure-adaptor

The upgraded version of the test report has complete functions and cool interface

Data sharing

The following is a 2021 software test data package I collected


Including: software testing learning route, software testing video materials, software testing related documents, software testing related tools, installation package, resume template of Senior Test Engineer, interview questions, simulated interview, PDF document, mind map, etc... I hope I can help you.

Pay attention to my WeChat official account: programmers are two black, and they can receive the above information package free of charge.

If you really want to do something, even if there are many obstacles, you will do everything possible to do it. But if you don't really want to accomplish something, even if the road ahead is smooth, you will try every reason to stop yourself from moving forward.

Recommended reading

High paid programmers can't escape the 35 year old level... How can we save ourselves when our ability is divorced from our age

Tsinghua sister has worked for a month to produce this 32W word Linux Knowledge Manual, which is marked as 31K on Github+

From the monthly salary of 3K for site internship to the annual salary of 30W for front-line enterprises, I caught up with myself who had been given high expectations

Keywords: software testing pytest

Added by greip on Thu, 20 Jan 2022 19:57:35 +0200