When I first met the Pytest automated testing framework, I fully understood it

Getting to know Pytest

  • Pytest is a testing tool implemented in Python that can be used for all types and levels of software testing.

  • Pytest is a test framework that can automatically find the use cases you write and output the results after running.

  • Features of pytest:

  • Is a command-line tool, easy to write use cases, strong readability
  • Very easy to use, easy to get started, rich in documentation, and there are many examples in the documentation for reference
  • Support unit testing and functional testing
  • Support parameterization
  • Some tests can be skipped during test execution, or some expected failure cases can be marked as failures
  • Cases that support repeated execution failures
  • Support running test cases written by unittest
  • Has many third-party plug-ins, and can be customized
  • Easy integration with continuous integration tools
  • Packages, files and methods that start or end with test can be run
  • You can use assert to assert
  • pytest-install
     
 pip install -U pytest -i https://pypi.tuna.tsinghua.edu.cn/simple

 

Pytest first example

  • By default, pytest can automatically recognize functions and methods beginning with test, as well as classes beginning with Test.
import pytest
import requests
 
 
def test_one():
    r = requests.get('https://www.baidu.com')
    print(r.status_code)
 
def test_two():
    r = requests.get('https://www.baidu.com')
    print(r.encoding)
 
class TestTask(object):
    def test_1(self):
        print('test 1')
 
    def test_2(self):
        print('test 2')
 
if __name__ == '__main__':
    pytest.main() # pytest

 How to run pytest

You can right-click and run directly through pytest.main() in the code, which is equivalent to python test_1.py; you can also start and run through the command line

How the code works

  • pytest.main()

While the current file will be executed, all py files starting with test in the path will be executed.
pytest first obtains the test functions in all files, and then executes.
The standard output (print, logging) in the code will not be displayed.

  • pytest.main(['-s'])

While the current file will be executed, all py files starting with test in the path will be executed.
pytest first obtains the test functions in all files, and then executes
the standard output (print, logging) in the display code.

  • pytest.main(['test_1.py'])

Only the current file will be executed and
the standard output (print, logging) in the code will not be displayed

  • pytest.main(['-s', 'test_1.py'])

Only the current file will be executed
and the standard output (print, logging) in the code will be displayed.
All the above four methods have corresponding command line operation methods

  • pytest

While the current file will be executed, all py files starting with test in the path will be executed.
pytest first obtains the test functions in all files, and then executes.
The standard output (print, logging) in the code will not be displayed.

  • pytest -s

While the current file will be executed, all py files starting with test in the path will be executed.
pytest first obtains the test functions in all files, and then executes
the standard output (print, logging) in the display code.

  • pytest test_1.py

Only the current file will be executed and
the standard output (print, logging) in the code will not be displayed

  • pytest -s test_1.py

Only the current file will be executed
Display the standard output in the code (print, logging)

Notice:

  • If the test class in the test script does not start with Test as the name, it will not be executed
  • If the function in the test script does not start with test, it will not be executed

Assertion handling

assert

what is an assertion

  • Assertions are used to check whether the program is correct, and if the condition is false, the execution of the program is interrupted.
  • When you write code, you always make some assumptions, and assertions are used to capture those assumptions in your code
  • Assertions are expressed as some boolean expressions

    Assertion usage scenarios

  • use assert statement
  • Assert expected exception
  • Assert expected warnings

assert assertion statement

  • When assert assertion is used, remark information can be added
  • When the assertion prediction fails, the remark information will be thrown as AssertionError and output on the console
assert expression [, arguments]
# expression 为 True 则 pass
# expression 为 False 则 抛出异常,有 argument 则输出 argument
'''
expression:
1)比较运算
2)逻辑运算  and | or | not
3)身份运算  is | is not
4)成员运算  in | not in
'''

 Sample code:

import requests
 
def test_assert():
    r = requests.get('http://www.baidu.com')
    assert r.status_code == 200, "没有返回200,断言失败"

 

exception assertion

  • During the test, when some methods are tested, it is predicted that some specific data will be input and some kind of exception will be thrown. If the exception occurs, the use case execution passes.

  • The assertion of this expected exception can be handled with pytest.raises() in pytest.

Use of pytest.raises()

  • pytest.raises(expected_exception, match=None, kwargs)
    expected_exception is expected exception class or exception class tuple
    match used to match exception message content

 

import pytest
 
 
def is_leap_year(year):
    # 先判断year是不是整型
    if isinstance(year, int) is not True:
        raise TypeError("传入的参数不是整数")
    elif year == 0:
        raise ValueError("公元元年是从公元一年开始!!")
    elif abs(year) != year:
        raise ValueError("传入的参数不是正整数")
    elif (year % 4 == 0 and year % 100 != 0) or year % 400 == 0:
        print("%d年是闰年" % year)
        return True
    else:
        print("%d年不是闰年" % year)
        return False
 
 
class TestAssert(object):
    """对判断是否是闰年的方法进行测试"""
 
    def test_exception_typeerror(self):
        # 使用 pytest.raises 作为上下文管理器,捕获给定异常类型TypeError
        # is_leap_year('2020')抛出TypeError,被pytest.raises捕获到,则测试用例执行通过
        with pytest.raises(TypeError):
            # 传入字符串引发类型错误
            is_leap_year('2020')

The result of the operation is as follows:

============================= test session starts ==============================
platform darwin -- Python 3.7.3, pytest-6.2.5, py-1.10.0, pluggy-1.0.0
rootdir: /Users/xiaoyidemac/Desktop/测试
plugins: tavern-1.16.3, openfiles-0.3.2, arraydiff-0.3, allure-pytest-2.9.45, doctestplus-0.3.0, remotedata-0.3.1collected 1 item
 
pytest_code.py                                                          [100%]
 
============================== 1 passed in 0.06s ===============================

Store the exception information in a variable

Sometimes we may need to use the exception information generated in the test. We can store the exception information in a variable. The type of the variable is the exception class, which contains information such as the type, value, or traceback of the exception.

import pytest
 
def is_leap_year(year):
    # 先判断year是不是整型
    if isinstance(year, int) is not True:
        raise TypeError("传入的参数不是整数")
    elif year == 0:
        raise ValueError("公元元年是从公元一年开始!!")
    elif abs(year) != year:
        raise ValueError("传入的参数不是正整数")
    elif (year % 4 == 0 and year % 100 != 0) or year % 400 == 0:
        print("%d年是闰年" % year)
        return True
    else:
        print("%d年不是闰年" % year)
        return False
 
class TestAssert(object):
    """对判断是否是闰年的方法进行测试"""
 
    def test_exception_typeerror(self):
        # 预测到参数不符合要求,会抛出TypeError异常;若出现该异常,则测试用例执行通过
        with pytest.raises(TypeError) as err_info:
            # 传入字符串引发类型错误
            is_leap_year('2020')
        # 断言异常的类型是 TypeError,断言成功则不会抛出异常
        assert err_info.type == TypeError, '错误类型不匹配'

Catch the exception by the content of the exception

import pytest
 
def is_leap_year(year):
    # 先判断year是不是整型
    if isinstance(year, int) is not True:
        raise TypeError("传入的参数不是整数")
    elif year == 0:
        raise ValueError("公元元年是从公元一年开始!!")
    elif abs(year) != year:
        raise ValueError("传入的参数不是正整数")
    elif (year % 4 == 0 and year % 100 != 0) or year % 400 == 0:
        print("%d年是闰年" % year)
        return True
    else:
        print("%d年不是闰年" % year)
        return False
 
 
class TestAssert(object):
    """对判断是否是闰年的方法进行测试"""
    def test_exception_typeerror(self):
        # 通过异常的内容捕捉异常,但具体是何种异常不清楚
        with pytest.raises(Exception, match='从公元一年开始') as err_info:
            is_leap_year(0)
        # 断言异常的类型是 ValueError,断言成功则不会抛出异常
        assert err_info.type == ValueError

warning assertion

  • The pytest.warns() method is used to assert warnings in pytest, and the assertion method is similar to pytest.raises().
  • In addition to asserting alarms, pytest.warns() can also capture alarm information and classify the captured alarm information. It can be set that when specific alarm information occurs, the use case execution fails.
  • When a warning is thrown in the program, the execution of the program will not be interrupted. Generally, the warning message is printed, but when the program throws an exception, the running of the program may be directly interrupted. Therefore, when we capture warning information, we can capture multiple ones, while exception information can only capture one.

Use of pytest.warns()

  • pytest.warns(expected_warning, *args, match, **kwargs)
    --- expected_warning expected warning class or warning class tuple, for example:
    DeprecationWarning deprecation warning
    UserWarning user warning
  • match can customize the matching warning content
import pytest
import warnings
 
def make_warn():
    # 抛出
    warnings.warn("deprecated", DeprecationWarning)
 
def not_warn():
    pass
 
def user_warn():
    warnings.warn("user warn", UserWarning)
 
 
class TestWarns(object):
    def test_make_warn(self):
        with pytest.warns(DeprecationWarning):
            make_warn()
 
    def test_not_warn(self):
        # 断言not_warn函数将会抛出警告
        # 但实际是没有抛出警告的,所以无法通过测试
        with pytest.warns(Warning):
            not_warn()
 
    def test_user_warn(self):
        with pytest.warns(UserWarning):
            user_warn()

The result of the operation is as follows:

 
================================================== test session starts ===================================================
platform linux -- Python 3.6.8, pytest-6.0.1, py-1.9.0, pluggy-0.13.1
rootdir: /home/python/code/unit_testing/pytest_code
collected 3 items                                                                                                        
 
test_7.py .F.                                                                                                      [100%]
 
======================================================== FAILURES ========================================================
________________________________________________ TestWarns.test_not_warn _________________________________________________
 
self = 
 
    def test_not_warn(self):
        with pytest.warns(Warning):
>           not_warn()
E           Failed: DID NOT WARN. No warnings of type (,) was emitted. The list of emitted warnings is: [].
 
test_7.py:24: Failed
================================================ short test summary info =================================================
FAILED test_7.py::TestWarns::test_not_warn - Failed: DID NOT WARN. No warnings of type (,) was emitted...
============================================== 1 failed, 2 passed in 0.04s ===============================================

store the warning message in a variable

Store the alarm information in a variable, and make an assertion by reading the information in this variable, including: the number of alarms, alarm information parameters, etc.

# pytest_code/test_8.py
import warnings
import pytest
 
def warn_message():
    warnings.warn("user", UserWarning)
    warnings.warn("runtime", RuntimeWarning)
 
def test_warn_match():
    with pytest.warns((UserWarning, RuntimeWarning)) as record:
        warn_message()
    assert len(record) == 2
    assert str(record[0].message) == "user"
    assert str(record[1].message) == "runtime"
    assert record[0].category == UserWarning
    assert record[1].category == RuntimeWarning

The result of the operation is as follows:

$ pytest test_8.py
================================================== test session starts ===================================================
platform linux -- Python 3.6.8, pytest-6.0.1, py-1.9.0, pluggy-0.13.1
rootdir: /home/python/code/unit_testing/pytest_code
collected 1 item                                                                                                         
 
test_8.py .                                                                                                        [100%]
 
=================================================== 1 passed in 0.01s ====================================================

Catch warnings by their content

# pytest_code/test_9.py
import warnings
import pytest
 
 
def make_warn():
    # 抛出
    warnings.warn("deprecated", DeprecationWarning)
 
 
def not_warn():
    pass
 
def user_warn():
    warnings.warn("user warn", UserWarning)
 
 
class TestWarns(object):
    def test_make_warn(self):
        # 捕获警告内容为deprecated的警告
        with pytest.warns(Warning, match='deprecated'):
            make_warn()

Finally, I would like to thank everyone who has read my article carefully. Reciprocity is always necessary. Although it is not a very valuable thing, you can take it away if you need it:

These materials should be the most comprehensive and complete preparation warehouse for [software testing] friends. This warehouse has also accompanied tens of thousands of test engineers through the most difficult journey, and I hope it can help you! Partners can click the small card below to receive 

Guess you like

Origin blog.csdn.net/okcross0/article/details/132278885