Python automation Excel

Posted by gassaz on Mon, 27 Dec 2021 21:52:59 +0100

What is the difference between the Pytest and Unittest test test frameworks?

How to distinguish the two is very simple. As an official testing framework, unittest is more basic in testing, and can be re developed on the basis of it. At the same time, the format will be more complex in usage; As a third-party framework, pytest framework is more flexible to use, and can have good compatibility with the original unittest style test cases. At the same time, it is richer in expansion. You can add scenarios through extended plug-ins, such as some concurrent tests;

Pytest installation

pip installation:

pip install pytest
 Copy code

Test installation succeeded:

pytest --help

py.test --help
 Copy code

Check installation version:

pytest --version
 Copy code

Pytest example

Rules for writing Pytest:

  • Test file to test_ Start (end with _test)
  • The Test class starts with Test;
  • The test method is test_ start
  • Assertions use the 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 code

Perform tests:

pytest test_example.py
 Copy code

Execution results:

C:\Users\libuliduobuqiuqiu\Desktop\GitProjects\PythonDemo\pytest>pytest test_example.py -v
================================================================= test session 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 in 0.16s ==================================================================
Copy code

remarks:

  • . Represents test pass, F represents test failure;
  • -v displays detailed test information, - h displays detailed help information of pytest command;

sign

By default, pytest looks for test in the current directory_ Is a test file that starts with _test (ends with _test), and executes all functions and methods in the file that start with test _ (ends with _test);

  1. Specifies to run the test case, which can be displayed by:: tag (file name:: Class Name:: method name) (file name:: function name)
pytest test_example3.py::test_odd
 Copy code
  1. Specify some test cases and test runs. You can use - k fuzzy matching
pytest -k example
 Copy code
  1. Pass pytest mark. Skip () or pytest makr. Skipif() conditional expression, skipping the specified test case
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 code
  1. Pass pytest Raise () captures the exceptions that the test case might throw
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 code
  1. You know in advance that the test example will fail, but you don't want to skip it. You need to display a prompt and 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 code
  1. Multiple groups of data are tested for test cases, and each group of parameters can be executed independently once (it can avoid stopping the test after a single group of data test fails in the test case)
@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 code

Firmware

Firmware is a preprocessing function. pytest will load and run these firmware before (or after) executing the test function. Common application scenarios include database connection and shutdown (device connection and shutdown)

Simple use

import pytest


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


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

According to the official explanation, when running the test function, it will first detect the parameters of the running function and search for fixture s with the same name as the parameters. Once pytest finds them, it will run these firmware, obtain the return values (if any) of these firmware, and pass these return values to the test function as parameters;

Pretreatment and post-treatment

Next, further verify the official statement:

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 code

Result of executing test function:

============================= test session 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
collecting ... collected 1 item

test_example.py::test_count Connect Database in .......
PASSED                                       [100%]Close Database out .......


============================== 1 passed in 0.07s ==============================
Copy code

remarks:

  • Firstly, the results verify the official explanation. pytest will find the firmware with the same name before executing the test function;
  • connect_ There is yield in DB firmware. By default, pytest will judge that the code before the yield keyword belongs to preprocessing and will be executed before the test. The code after yield belongs to post-processing and will be executed after the test;

Scope

We have learned about the function of firmware from the above, and extracted some repetitive work to facilitate reuse. At the same time, in order to control firmware more finely in pytest framework, we will use scope to specify the scope of use of firmware, The first mock exam is the first mock exam in the module. The more specific example is the connection of the database. The operation of this connection may be time-consuming. I only need to run the test function of this module once, without running every time.

In defining firmware, the function is generally declared through the scop parameter. Commonly used are:

  • Function: function level, each test function will execute firmware once;
  • Class: class level, each test class is executed once, and all methods can be used;
  • Module: module level, each module is executed once, and the functions and methods in the module can be used;
  • Session: session level. A test is executed only once. 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 code

Execution results:

============================= test session 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
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 passed in 0.07s ==============================
Copy code

It can be seen from here that the firmware of module and session scope is only executed once, which can verify the official introduction

Automatic execution

Some people may say that it's so troublesome. Setting up directly in the unittest framework can automatically perform preprocessing, and the same pytest framework has similar automatic execution; The firmware in pytest framework generally runs automatically through the parameter autouse.

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 code

Execution results:

============================= test session 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
collecting ... collected 2 items

test_example.py::test1 Connect Database in .......
PASSED                                            [ 50%]test1

test_example.py::test2 PASSED                                            [100%]test
Close Database out .......


============================== 2 passed in 0.07s ==============================
Copy code

The results show that the test function automatically executes connect before and after running_ DB firmware;

Parameterization

I mentioned @ pytest mark. Parameterize passes the parameterization test. When the firmware passes in parameters, it needs to pass the firmware request built in the pytest framework and pass the request Param get 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 code

Execution results:

============================= test session 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
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 passed in 0.07s ==============================
Copy code

Here, simulate the connection between redis and elasticsearch, load the firmware, automatically execute the connection, and then execute the test function to disconnect.

summary

For development, why should we also learn automated testing? A very important point is to save some repeated work time through automated testing. At the same time, it is of great significance for optimizing code structure, improving code coverage and subsequent project reconstruction, At the same time, understanding the difference between pytest and unittest on the basis of is helpful to choose the test tools suitable for you in different business scenarios. This article only briefly introduces the basic use of pytest. If you are interested, you can look at the official documents. The official documents also mention the use of built-in firmware, common test scenarios and so on.

reference resources:

docs.pytest.org/en/6.2.x/co... learning-pytest.readthedocs.io/zh/latest/d...