What is the difference between Pytest and Unittest?

How to distinguish between the two is very simple unitTest as the official testing framework is more basic in terms of testing and can be built on again

Secondary development, and more complex formatting in usage; Pytest framework, as a third-party framework, is convenient in that it is more flexible in use and has good compatibility with the original UnitTest style test cases. At the same time, it is more rich in extension, which can increase the use of scenarios through extended plug-ins, such as some concurrent tests.

Pytest installation

PIP installation:

pip install pytest
Copy the code

Test installation successful:

pytest --help

py.test --help
Copy the code

Check the installed version:

pytest --version
Copy the code

Pytest sample

Pytest writing rules:

  • The test file starts with test_ (ends with _test)
  • The Test class starts with Test;
  • The test method starts with test_
  • Assert using basic Assert

test_example.py

def count_num(a: list) - >int:
    return len(a)


def test_count() :
    assert count_num([1.2.3]) != 3
Copy the code

Perform tests:

pytest test_example.py
Copy the code

Execution Result:

C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest>pytest test_example.py -v = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =testSession starts = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = platform win32 - Python 3.6.8, Pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- d:\coding\python3.6\python.exe cacheDir:.pytest_Cache Rootdir: C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest plugins: Faker-8.11.0 COLLECTED 1 item test_example.py::test_count FAILED [100%] ====================================================================== FAILURES = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = _____________________________________________________________________ test_count ______________________________________________________________________ def test_count(): > assert count_num([1, 2, 3]) ! = 3 E assert 3 ! = 3 E +where 3 = count_num([1, 2, 3])

test_example.py:11: AssertionError
=============================================================== short test summary info ===============================================================
FAILED test_example.py::test_count - assert 3 != 3
================================================================== 1 failed in0.16 s = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =Copy the code

Remark:

  • . Indicates that the test passes. F indicates that the test fails.
  • -v displays detailed test information. -h displays detailed help information about the Pytest command.

tag

By default, PyTest looks for test files starting with test_ (ending with _test) in the current directory and executes all functions and methods in the file starting with test_ (ending with _test).

  1. Specify to run the test case by displaying flags (file name :: class name :: method name) (file name :: function name) with ::
pytest test_example3.py::test_odd
Copy the code
  1. Specify some test case test runs that can use -k fuzzy matching
pytest -k example
Copy the code
  1. Skip the specified test case with pytest.mark.skip() or pytest.makr.skipIf () conditional expression
import pytest

test_flag = False

@pytest.mark.skip()
def test_odd() :
    num = random.randint(0.100)
    assert num % 2= =1


@pytest.mark.skipif(test_flag is False, reason="test_flag is False")
def test_even() :
    num = random.randint(0.1000)
    assert num % 2= =0
Copy the code
  1. Catch exceptions that the test case might throw by pytest.raises()
def test_zero() :
    num = 0
    with pytest.raises(ZeroDivisionError) as e:
        num = 1/0
    exc_msg = e.value.args[0]
    print(exc_msg)
    assert num == 0
Copy the code
  1. Knowing in advance that the test case fails, but not wanting to skip it, to display a prompt, use Pytest.mark.xfail ()
@pytest.mark.xfail()
def test_sum() :
    random_list = [random.randint(0.100)  for x in range(10)]
    num = sum(random_list)
    assert num < 20
Copy the code
  1. Multiple groups of data tests are performed on test cases, and each group of parameters can be independently executed once (to avoid stopping the test after a single group of data test fails to be executed internally)
@pytest.mark.parametrize('num,num2', [(1.2), (3.4)])
def test_many_odd(num: int, num2: int) :
    assert num % 2= =1
    assert num2 % 2= =0
Copy the code

The firmware (Fixture)

Firmware is pre-processed functions that pyTest loads and runs before (or after) the test function is executed. Common applications include database connection and shutdown (device connection and shutdown).

Simple to use

import pytest


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


def test_count(postcode) :
    assert postcode == "hello"
Copy the code

When you run a test function, you first detect the parameters of the running function, search for fixtures with the same name as the parameters, and when PyTest finds it, it runs the firmware and retrieves its return value (if any).

And pass these return values as parameters to the test function;

Pre-treatment and post-treatment

Here’s a closer look at the official line:

import pytest


@pytest.fixture()
def connect_db() :
    print("Connect Database in .......")
    yield
    print("Close Database out .......")


def read_database(key: str) :
    p_info = {
        "name": "zhangsan"."address": "China Guangzhou"."age": 99
    }
    return p_info[key]


def test_count(connect_db) :
    assert read_database("name") = ="zhangsan"
Copy the code

Result of executing test function:

= = = = = = = = = = = = = = = = = = = = = = = = = = = = =testSession starts = = = = = = = = = = = = = = = = = = = = = = = = = = = = = platform win32 - Python 3.6.8, pytest - 6.2.5, py - 1.10.0, Plugy-1.0.0 -- D:\Coding\Python3.6\python.exe cachedir:.pytest_cache Rootdir: C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest plugins: faker-8.11.0 collecting... collected 1 item test_example.py::test_count Connect Databasein. PASSED [100%]Close Database out ....... ============================== 1 passedin0.07 s = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =Copy the code

Remark:

  • Pytest will look for the firmware with the same name before executing the test function.
  • Connect_db firmware has yield, where PyTest by default determines that code before yield is preprocessed and will be executed before the test, and code after yield is postprocessed and will be executed after the test.

scope

From the previous general understanding of the role of firmware, remove some repetitive work to facilitate reuse, at the same time in the PyTest framework in order to more refined control firmware,

Will use scope to specify the use range of the firmware, (such as test functions in a module performs a can, do not need to repeat) module of function more specific example is the database connection, a connection operation is likely to be time-consuming, I just need to test functions in this module can be run at a time, do not need to run every time.

The definition of firmware is generally declared through the scop parameter, commonly used are:

  • Function: at the function level, each test function executes the firmware once;
  • Class: Class level, where each test class is executed once and all methods are available;
  • Module: at the module level, each module is executed once, and functions and methods in the module can be used;
  • Session: At the session level, a test is executed only once and all found functions and methods are available.
import pytest


@pytest.fixture(scope="function")
def func_scope() :
    print("func_scope")


@pytest.fixture(scope="module")
def mod_scope() :
    print("mod_scope")


@pytest.fixture(scope="session")
def sess_scope() :
    print("session_scope")


def test_scope(sess_scope, mod_scope, func_scope) :
    pass


def test_scope2(sess_scope, mod_scope, func_scope) :
    pass
Copy the code

Execution Result:

= = = = = = = = = = = = = = = = = = = = = = = = = = = = =testSession starts = = = = = = = = = = = = = = = = = = = = = = = = = = = = = platform win32 - Python 3.6.8, pytest - 6.2.5, py - 1.10.0, Plugy-1.0.0 -- D:\Coding\Python3.6\python.exe cachedir:.pytest_cache Rootdir: C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest plugins: faker-8.11.0 collecting... collected 2 items test_example2.py::test_scope session_scope mod_scope func_scope PASSED [ 50%] test_example2.py::test_scope2 func_scope PASSED [100%] ============================== 2 passedin0.07 s = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =Copy the code

The module, session-scoped firmware is executed only once, which verifies the official usage instructions

automated

One might say, why is this so troublesome, when the UnitTest framework defines setUp directly and automatically preprocesses, again

The PyTest framework has similar automatic execution; Firmware in the PyTest framework is usually automatically run by the autouse control parameter.

import pytest


@pytest.fixture(scope='session', autouse=True)
def connect_db() :
   print("Connect Database in .......")
   yield
   print("Close Database out .......")


def test1() :
   print("test1")


def test2() :
   print("test")
Copy the code

Execution Result:

= = = = = = = = = = = = = = = = = = = = = = = = = = = = =testSession starts = = = = = = = = = = = = = = = = = = = = = = = = = = = = = platform win32 - Python 3.6.8, pytest - 6.2.5, py - 1.10.0, Plugy-1.0.0 -- D:\Coding\Python3.6\python.exe cachedir:.pytest_cache Rootdir: C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest plugins: faker-8.11.0 collecting... collected 2 items test_example.py::test1 Connect Databasein. PASSED [ 50%]test1 test_example.py::test2 PASSED [100%]test
Close Database out .......


============================== 2 passed in0.07 s = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =Copy the code

The connect_DB firmware is automatically executed before and after the test function is run.

A parameterized

As mentioned briefly, @pytest.Mark.Parametrize passes the parameterization test, as it will pass parameters for firmware

Pytest framework built-in firmware request, and request. Param to obtain the parameters

import pytest


@pytest.fixture(params=[
    ('redis'.'6379'),
    ('elasticsearch'.'9200')])
def param(request) :
    return request.param


@pytest.fixture(autouse=True)
def db(param) :
    print('\nSucceed to connect %s:%s' % param)

    yield

    print('\nSucceed to close %s:%s' % param)


def test_api() :
    assert 1= =1
Copy the code

Execution Result:

= = = = = = = = = = = = = = = = = = = = = = = = = = = = =testSession starts = = = = = = = = = = = = = = = = = = = = = = = = = = = = = platform win32 - Python 3.6.8, pytest - 6.2.5, py - 1.10.0, Plugy-1.0.0 -- D:\Coding\Python3.6\python.exe cachedir:.pytest_cache Rootdir: C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest plugins: faker-8.11.0 collecting... collected 2 items test_example.py::test_api[param0] Succeed to connect redis:6379 PASSED [ 50%] Succeed to close redis:6379 test_example.py::test_api[param1] Succeed to connect elasticsearch:9200 PASSED [100%] Succeed to close elasticsearch:9200 ============================== 2 passedin0.07 s = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =Copy the code

Connect redis to ElasticSearch, load the firmware and connect automatically, then execute the test function and disconnect.

conclusion

For development, it is important to learn automatic testing. It is important to save time of repetitive work through automatic testing, and it is also of great significance to optimize code structure, improve code coverage and subsequent project reconstruction. Understanding the fundamental differences between PyTest and UnitTest can help you choose the right test tool for different business scenarios. This article is just a brief introduction to the basic use of PyTest. If you are interested in pyTest, please refer to the official documentation, which also mentions the use of built-in firmware, common test scenarios and so on.

Reference:

Docs.pytest.org/en/6.2.x/co… Learning – pytest. Readthedocs. IO/useful/latest/d…