【pytest单元测试框架】(2)pytest 的基本使用方法

pytest 的基本使用方法

1、断言

  在 unittest 单元测试框架中提供了丰富的断言方法,如 assertEqual()、assertIn()、assertTrue()、assertIs()等。pytest 单元测试框架并没有提供专门的断言方法,而是直接使用Python 的 assert 进行断言。   创建 test_assert.py 文件
# -*- coding:utf-8 -*-
# filename: test_assert.py
# author: hello.yin
# date: 2021/11/18

import pytest

# 创建用于测试的功能函数


# 加法测试
def add(a, b):
    return a + b


# 判断素数
def is_prime(n):
    if n < 2:
        return False
    else:
        for i in range(2, n):
            if n % i == 0:
                return False
            else:
                return True


# 判断相等
def test_add1():
    assert add(3, 5) == 8

# 判断大于
def test_add2():
    assert add(5, 5) > 10


# 判断为True
def test_isprime1():
    assert is_prime(1) is False


# 判断为False
def test_isprime2():
    assert is_prime(7) is True


# 判断包含
def test_in():
    a = "he"
    b = "hello"
    assert a in b


# 判断不包含
def test_not_in():
    a = "yin"
    b = "hello"
    assert a not in b


if __name__ == "__main__":
    pytest.main()

   执行结果:

(base_practice) D:\00test\base_practice\pyTest>pytest test_assert.py
=============================================================== test session starts ===============================================================
platform win32 -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: D:\00test\base_practice\pyTest
collected 6 items                                                                                                                                  

test_assert.py .F....                                                                                                                        [100%]

==================================================================== FAILURES =====================================================================
____________________________________________________________________ test_add2 ____________________________________________________________________

    def test_add2():
>       assert add(5, 5) > 10
E    assert 10 > 10
E     +  where 10 = add(5, 5)

test_assert.py:34: AssertionError
============================================================= short test summary info =============================================================
FAILED test_assert.py::test_add2 - assert 10 > 10
=========================================================== 1 failed, 5 passed in 0.06s ===========================================================
  上面的例子展示了 pytest 断言的用法,借助 Python 的运算符号和关键字即可轻松实现不同数据类型的断言。

 2、Fixture

  Fixture 通常用来对测试方法、测试函数、测试类和整个测试文件进行初始化或还原测试环境。创建 test_fixtures_01.py 文件。
# -*- coding:utf -8 -*-
# filename: /pyTest/test_fixtrue.py
# author: hello.yin
# date: 2021/11/18


# 功能函数
def mul(a, b):
    return a*b


# 模块级别
def setup_module(module):
    print("===========setup_module=========")


def teardown_module(module):
    print("============teardown_module========")


# 函数级别
def setup_function(function):
    print("==========setup_founction=========")


def teardown_function(function):
    print("==========setup_founction==========")


# 用例级别
def setup():
    print("==========setup=========")


def teardown():
    print("=========teardowm==========")


# 测试用例
def test_35():
    assert mul(3, 5) == 14

def test_45():
    assert mul(4, 5) == 20
  这里主要用到模块级别和函数级别的 Fixture。
  • setup_module/teardown_module:在当前文件中,在所有测试用例执行之前与之后执行。
  • setup_function/teardown_function:在每个测试函数之前与之后执行。
  • setup/teardown:在每个测试函数之前与之后执行。这两个方法同样可以作用于类方法。

  执行结果:

(base_practice) D:\00test\base_practice\pyTest>pytest test_fixtrue01.py
=============================================================== test session starts ===============================================================
platform win32 -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: D:\00test\base_practice\pyTest
collected 2 items                                                                                                                                  

test_fixtrue01.py F.                                                                                                                         [100%]

==================================================================== FAILURES =====================================================================
_____________________________________________________________________ test_35 _____________________________________________________________________

    def test_35():
>       assert mul(3, 5) == 14
E    assert 15 == 14
E     +  where 15 = mul(3, 5)

test_fixtrue01.py:41: AssertionError
-------------------------------------------------------------- Captured stdout setup --------------------------------------------------------------
===========setup_module=========
==========setup_founction=========
==========setup=========
------------------------------------------------------------ Captured stdout teardown -------------------------------------------------------------
=========teardowm==========
==========setup_founction==========
============================================================= short test summary info =============================================================
FAILED test_fixtrue01.py::test_35 - assert 15 == 14
=========================================================== 1 failed, 1 passed in 0.05s ===========================================================

  pytest 是支持使用测试类的,同样必须以“Test”开头,注意首字母大写。在引入测试类的情况下,Fixture 的用法如下。创建 test_fixtures_02.py 文件。

# -*- coding:utf-8 -*-
# filename: test_fixtrue02.py
# author: hello.yin
# date: 2021/11/18


# 功能函数
def mul(a, b):
    return a*b


# 判断函数类
class TestMul:

    @classmethod
    def setup_class(cls):
        print("===============setup_class===============")

    @classmethod
    def teardown_class(cls):
        print("===============teardown_class==============")

    @staticmethod
    def setup_method(self):
        print("==============setup_method=================")

    @staticmethod
    def teardown_method(self):
        print("==============teardown_method==============")

    @staticmethod
    def setup():
        print("==========setup=============")

    @staticmethod
    def teardown():
        print("===========teardown============")

    # 测试用例
    def test_mul35(self):
        assert mul(3, 5) == 16

    def test_mul45(self):
        assert mul(4, 5) == 20
这里主要用到类级别和方法级别的 Fixture。
  • setup_class/teardown_class :在当前测试类的开始与结束时执行。
  • setup_method/teardown_method :在每个测试方法开始与结束时执行。
  • setup/teardown :在每个测试方法开始与结束时执行,同样可以作用于测试函数。
运行结果:
(base_practice) D:\00test\base_practice\pyTest>pytest test_fixtrue02.py
=============================================================== test session starts ===============================================================
platform win32 -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: D:\00test\base_practice\pyTest
collected 2 items                                                                                                                                  

test_fixtrue02.py F.                                                                                                                         [100%]

==================================================================== FAILURES =====================================================================
_______________________________________________________________ TestMul.test_mul35 ________________________________________________________________

self = <pyTest.test_fixtrue02.TestMul object at 0x0000026A7E40D108>

    def test_mul35(self):
>       assert mul(3, 5) == 16
E    assert 15 == 16
E     +  where 15 = mul(3, 5)

test_fixtrue02.py:41: AssertionError
-------------------------------------------------------------- Captured stdout setup --------------------------------------------------------------
===============setup_class===============
==============setup_method=================
==========setup=============
------------------------------------------------------------ Captured stdout teardown -------------------------------------------------------------
===========teardown============
==============teardown_method==============
============================================================= short test summary info =============================================================
FAILED test_fixtrue02.py::TestMul::test_mul35 - assert 15 == 16
=========================================================== 1 failed, 1 passed in 0.05s ===========================================================

 3、 参数化

当一组测试用例有固定的测试数据时,就可以通过参数化的方式简化测试用例的编写。pytest 本身是支持参数化的,不需要额外安装插件。创建 test_parameterize.py 文件。
# -*- coding:utf-8 -*-
# filename: test_parameterized.py
# author: hello.yin
# date: 20221/11/18

import pytest
import math


@pytest.mark.parametrize(
    "base, exponent, expected",
    ((2, 2, 4),
     (3, 3, 9),
     (2, 3, 8),
     (0, 9, 0)),
    ids=["case1", "case2", "case3", "case4"])
def test_row(base, exponent, expected):
    assert math.pow(base, exponent) == expected

  用法与 unittest 的参数化插件类似,通过 pytest.mark.parametrize()方法设置参数。“base,exponent,expected”用来定义参数的名称。通过数组定义参数时,每一个元组都是一条测试用例使用的测试数据。ids 参数默认为 None,用于定义测试用例的名称。math 模块的 pow()方法用于计算 xy(x 的 y 次方)的值。

  运行结果如下:
(base_practice) D:\00test\base_practice\pyTest>pytest -v test_parameterized.py
=============================================================== test session starts ===============================================================
platform win32 -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 -- C:\Users\yzp\PycharmProjects\base_practice\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\00test\base_practice\pyTest
collected 4 items                                                                                                                                  

test_parameterized.py::test_row[case1] PASSED                                                                                                [ 25%]
test_parameterized.py::test_row[case2] FAILED                                                                                                [ 50%]
test_parameterized.py::test_row[case3] PASSED                                                                                                [ 75%]
test_parameterized.py::test_row[case4] PASSED                                                                                                [100%]

==================================================================== FAILURES =====================================================================
_________________________________________________________________ test_row[case2] _________________________________________________________________

base = 3, exponent = 3, expected = 9

    @pytest.mark.parametrize(
        "base, exponent, expected",
        ((2, 2, 4),
         (3, 3, 9),
         (2, 3, 8),
         (0, 9, 0)),
        ids=["case1", "case2", "case3", "case4"])
    def test_row(base, exponent, expected):
>       assert math.pow(base, exponent) == expected
E    assert 27.0 == 9
E      +27.0
E      -9

test_parameterized.py:18: AssertionError
============================================================= short test summary info =============================================================
FAILED test_parameterized.py::test_row[case2] - assert 27.0 == 9
=========================================================== 1 failed, 3 passed in 0.04s ===========================================================

 4、 运行测试

  pytest 提供了丰富的参数运行测试用例,在前面的例子中已经使用到一些参数,例如,“-s”参数用于关闭捕捉,从而输出打印信息;“-v”参数用于增加测试用例冗长。    通过“pytest --help”可以查看帮助:
 pytest––help
  pytest 提供的参数比较多,下面只介绍常用的参数:

4.1.运行名称中包含某字符串的测试用例

  在前面内容test_assert.py 文件,其中有 42条是关于 add()功能的,并且在测试用例的名称上包含了“add”字符串,因此这里可以通过“-k”来指定在名称中包含“add”的测试用例。
(base_practice) D:\00test\base_practice\pyTest>pytest -k add test_assert.py
=============================================================== test session starts ===============================================================
platform win32 -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: D:\00test\base_practice\pyTest
collected 6 items / 4 deselected / 2 selected                                                                                                      

test_assert.py .F                                                                                                                            [100%]

==================================================================== FAILURES =====================================================================
____________________________________________________________________ test_add2 ____________________________________________________________________

    def test_add2():
>       assert add(5, 5) > 10
E    assert 10 > 10
E     +  where 10 = add(5, 5)

test_assert.py:34: AssertionError
============================================================= short test summary info =============================================================
FAILED test_assert.py::test_add2 - assert 10 > 10
==================================================== 1 failed, 1 passed, 4 deselected in 0.04s ====================================================

4.2.减少测试的运行冗长

这一次运行日志少了很多信息,“-q”用来减少测试运行的冗长;也可以使用“--quiet”代替
(base_practice) D:\00test\base_practice\pyTest>pytest -q  test_assert.py
.F....                                                                                                                                       [100%]
==================================================================== FAILURES =====================================================================
____________________________________________________________________ test_add2 ____________________________________________________________________

    def test_add2():
>       assert add(5, 5) > 10
E    assert 10 > 10
E     +  where 10 = add(5, 5)

test_assert.py:34: AssertionError
============================================================= short test summary info =============================================================
FAILED test_assert.py::test_add2 - assert 10 > 10
1 failed, 5 passed in 0.03s

4.3. 如果出现一条测试用例失败,则退出测试

  这在测试用例的调试阶段是有用的,当出现一条失败的测试用例时,应该先通过调试让这条测试用例运行通过,而不是继续执行后面的测试用例。
(base_practice) D:\00test\base_practice\pyTest>pytest -x  test_assert.py
=============================================================== test session starts ===============================================================
platform win32 -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: D:\00test\base_practice\pyTest
collected 6 items                                                                                                                                  

test_assert.py .F

==================================================================== FAILURES =====================================================================
____________________________________________________________________ test_add2 ____________________________________________________________________

    def test_add2():
>       assert add(5, 5) > 10
E    assert 10 > 10
E     +  where 10 = add(5, 5)

test_assert.py:34: AssertionError
============================================================= short test summary info =============================================================
FAILED test_assert.py::test_add2 - assert 10 > 10
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
=========================================================== 1 failed, 1 passed in 0.04s ===========================================================

 4.4.运行测试目录

  测试目录既可以指定相对路径(如 ./test_dir ) , 也 可以指定绝对路径(如D:\00test\base_practice\pyTest)
(base_practice) D:\00test\base_practice\pyTest>pytest D:\00test\base_practice\pyTest
=============================================================== test session starts ===============================================================
platform win32 -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: D:\00test\base_practice\pyTest
collected 15 items                                                                                                                                 

test_assert.py .F....                                                                                                                        [ 40%]
test_fixtrue01.py F.                                                                                                                         [ 53%]
test_fixtrue02.py F.                                                                                                                         [ 66%]
test_parameterized.py .F..                                                                                                                   [ 93%]
test_sample.py F                                                                                                                             [100%]

==================================================================== FAILURES =====================================================================
____________________________________________________________________ test_add2 ____________________________________________________________________

    def test_add2():
>       assert add(5, 5) > 10
E    assert 10 > 10
E     +  where 10 = add(5, 5)

test_assert.py:34: AssertionError
_____________________________________________________________________ test_35 _____________________________________________________________________

    def test_35():
>       assert mul(3, 5) == 14
E    assert 15 == 14
E     +  where 15 = mul(3, 5)

test_fixtrue01.py:41: AssertionError
-------------------------------------------------------------- Captured stdout setup --------------------------------------------------------------
===========setup_module=========
==========setup_founction=========
==========setup=========
------------------------------------------------------------ Captured stdout teardown -------------------------------------------------------------
=========teardowm==========
==========setup_founction==========
_______________________________________________________________ TestMul.test_mul35 ________________________________________________________________

self = <pyTest.test_fixtrue02.TestMul object at 0x00000276153A5F88>

    def test_mul35(self):
>       assert mul(3, 5) == 16
E    assert 15 == 16
E     +  where 15 = mul(3, 5)

test_fixtrue02.py:41: AssertionError
-------------------------------------------------------------- Captured stdout setup --------------------------------------------------------------
===============setup_class===============
==============setup_method=================
==========setup=============
------------------------------------------------------------ Captured stdout teardown -------------------------------------------------------------
===========teardown============
==============teardown_method==============
_________________________________________________________________ test_row[case2] _________________________________________________________________

base = 3, exponent = 3, expected = 9

    @pytest.mark.parametrize(
        "base, exponent, expected",
        ((2, 2, 4),
         (3, 3, 9),
         (2, 3, 8),
         (0, 9, 0)),
        ids=["case1", "case2", "case3", "case4"])
    def test_row(base, exponent, expected):
>       assert math.pow(base, exponent) == expected
E    assert 27.0 == 9
E     +  where 27.0 = <built-in function pow>(3, 3)
E     +    where <built-in function pow> = math.pow

test_parameterized.py:18: AssertionError
___________________________________________________________________ test_answer ___________________________________________________________________

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

test_sample.py:13: AssertionError
============================================================= short test summary info =============================================================
FAILED test_assert.py::test_add2 - assert 10 > 10
FAILED test_fixtrue01.py::test_35 - assert 15 == 14
FAILED test_fixtrue02.py::TestMul::test_mul35 - assert 15 == 16
FAILED test_parameterized.py::test_row[case2] - assert 27.0 == 9
FAILED test_sample.py::test_answer - assert 4 == 5
========================================================== 5 failed, 10 passed in 0.13s ===========================================================

 4.5.指定特定类或方法执行

这里指定运行 test_fixtures_02.py 文件中 TestMul 类下的test_mul35()方法,文件名、类名和方法名之间用“::”符号分隔
(base_practice) D:\00test\base_practice\pyTest>pytest test_fixtrue02.py::TestMul::test_mul35
=============================================================== test session starts ===============================================================
platform win32 -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: D:\00test\base_practice\pyTest
collected 1 item                                                                                                                                   

test_fixtrue02.py F                                                                                                                          [100%]

==================================================================== FAILURES =====================================================================
_______________________________________________________________ TestMul.test_mul35 ________________________________________________________________

self = <pyTest.test_fixtrue02.TestMul object at 0x0000027E785C9808>

    def test_mul35(self):
>       assert mul(3, 5) == 16
E    assert 15 == 16
E     +  where 15 = mul(3, 5)

test_fixtrue02.py:41: AssertionError
-------------------------------------------------------------- Captured stdout setup --------------------------------------------------------------
===============setup_class===============
==============setup_method=================
==========setup=============
------------------------------------------------------------ Captured stdout teardown -------------------------------------------------------------
===========teardown============
==============teardown_method==============
===============teardown_class==============
============================================================= short test summary info =============================================================
FAILED test_fixtrue02.py::TestMul::test_mul35 - assert 15 == 16
================================================================ 1 failed in 0.04s ================================================================

 4.6.通过 main()方法运行测试

import pytest
    if __name__ == '__main__':
    pytest.main(['-s', './test_dir'])
创建 run_tests.py 文件,在文件中通过数组指定参数,每个参数为数组中的一个元素。

5、生成测试报告

pytest 支持生成多种格式的测试报告。

5.1 生成 JUnit XML 文件

> pytest ./test_dir --junit-xml=./report/log.xml

 XML 类型的日志主要用于存放测试结果,方便我们利用里面的数据定制自己的测试报告。XML 格式的测试报告如图所示。

执行结果:

(base_practice) D:\00test\base_practice\pyTest>pytest test_assert.py --junit-xml=./report/log.xml
=============================================================== test session starts ===============================================================
platform win32 -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0
rootdir: D:\00test\base_practice\pyTest
collected 6 items                                                                                                                                  

test_assert.py .F....                                                                                                                        [100%]

==================================================================== FAILURES =====================================================================
____________________________________________________________________ test_add2 ____________________________________________________________________

    def test_add2():
>       assert add(5, 5) > 10
E    assert 10 > 10
E     +  where 10 = add(5, 5)

test_assert.py:34: AssertionError
---------------------------------------- generated xml file: D:\00test\base_practice\pyTest\report\log.xml ----------------------------------------
============================================================= short test summary info =============================================================
FAILED test_assert.py::test_add2 - assert 10 > 10
=========================================================== 1 failed, 5 passed in 0.06s ===========================================================

【pytest单元测试框架】(2)pytest 的基本使用方法

6、conftest.py

  conftest.py 是 pytest 特有的本地测试配置文件,既可以用来设置项目级别的 Fixture,也可以用来导入外部插件,还可以用来指定钩子函数。   创建 test_project/conftest.py 测试配置文件。   需要说明的是,conftest.py 只作用于它所在的目录及子目录。 【pytest单元测试框架】(2)pytest 的基本使用方法

  创建 test_project/conftest.py 测试配置文件

import pytest

@pytest.fixture()
def test_url():
    return "https://www.baidu.com"

  创建 test_project/test_sub.py 测试用例文件

def test_baidu(test_url):
    print(test_url)
  这里创建的函数可以直接调用 conftest.py 文件中的 test_url()钩子函数,测试结果如下
(base_practice) D:\00test\base_practice\pyTest>pytest -v -s test_project\
=============================================================== test session starts ===============================================================
platform win32 -- Python 3.7.4, pytest-6.2.5, py-1.11.0, pluggy-1.0.0 -- C:\Users\yzp\PycharmProjects\base_practice\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\00test\base_practice\pyTest
collected 1 item                                                                                                                                   

test_project/test_sub.py::test_baidu https://www.baidu.com
PASSED

================================================================ 1 passed in 0.01s ================================================================
上一篇:获取pc硬件信息杂记


下一篇:【BZOJ1915】[Usaco2010 Open]奶牛的跳格子游戏 DP+单调队列