pytest测试框架简单使用

一、简介

1.1 pytest与unittest区别

  1. unittest
    测试文件必须先import unittest
    测试类必须继承unittest.TestCase
    测试方法必须以test_开头
    测试类必须有unittest.main()方法
    unittest只有setup/teardown装载测试用例
    2.pytest
    测试文件名以“test_”开通
    测试类以“Test”开头,且不能有init方法
    测试方法以"test_开头"
    除了setup/teardown还能自定义夹具fixture装载测试用例
    断言直接使用assert

1.2 简介

安装

pip install -U pytest

查看版本号

pytest --version

1.3 简单示例

有测试用例case1.py

def add(a, b):
    c = a + b
    return c
def sub(a, b):
    c = a - b
    return c

def test_add():
    #  测试加法
    assert add(1, 1) == 2

def test_sub():
    # 测试减法  
    assert sub(8, 5) == 6

执行

pytest case1.py

结果:


image.png

二、pytest的调用和使用

2.1 规则

1.测试文件必须以test_开头或者_test结尾
2.测试类必须以Test开头,且不能有init方法
3.测试方法必须以test开头
4.断言必须使用assert

2.2 运行pytest可能导致的六种不同退出代码

退出代码0:所有测试都已收集并成功通过
退出代码1:测试已收集并运行,但有些测试失败
退出代码2:测试执行被用户中断
退出代码3:执行测试时发生内部错误
退出代码4:pytest命令行使用错误
退出代码5:未收集任何测试

2.3获取版本、选项、环境变量信息

查看版本

pytest --version

显示可用内置方法

pytest --fixtures

显示命令行及配置文件帮助信息

pytest -h --help

2.4 指定/选择测试用例

在模块中运行测试用例

pytest test_mod.py

在目录文件中执行测试

pytest testing/

按关键字表达式执行测试
运行包含指定表达式匹配的名称用例,其中包含文件名、类名、函数名作为变量且支持python运算符(and/or )操作。

pytest -k "Myclass and not method"

会执行TestMyclass.test_something但不会执行TestMyclass.test_method_other
示例如下,有case1.py模块,有测试类My,其中有两个测试用例test_add()和test_sub()

import pytest
def add(a, b):
    c = a + b
    return c
def sub(a, b):
    c = a - b
    return c
class TestMy:
    @staticmethod
    def test_add():
        #  测试加法
        assert add(1, 1) == 2
    @staticmethod
    def test_sub():
    # 测试减法
        assert sub(8, 5) == 4

执行

 pytest -k "My and not sub" case1.py

结果:
即通过关键字不执行My类中的sub测试方法,所以只执行了add测试方法。

image.png

按节点ID执行
每次执行收集到的测试用例集合都会被分配一个唯一节点id(nodeid),其中包含模块名,后跟说明符,如类名、函数名及参数,由::字符分隔。执行模块中某条指定的测试用例,如

pytest test_mod.py::test_func

执行模块中某个类下的用例,如

pytest test_mod.py::TestClass::test_method

通过标记表达式运行

pytest -m slow

执行所有使用@pytest.mark.slow装饰的用例
如case1.py中装饰sub用例

def add(a, b):
    c = a + b
    return c
def sub(a, b):
    c = a - b
    return c
def test_add():
    #  测试加法
    assert add(1, 1) == 3

@pytest.mark.slow
def test_sub():
    # 测试减法
    assert sub(8, 5) == 4

执行

pytest -m slow case1.py

结果


image.png

第1/N次失败即停止测试

pytest -x   # 出现失败即停止
pytest --maxfail=n  # 出现n次失败就停止测试

从包中运行测试

pytest --pyargs pkg.testing

即从pkg.testing包中根据命名规则查找执行测试

2.5 详细测试结果摘要

"-r"标志可用于在测试完成后显示测试摘要信息,后面可跟其他标记获取所有失败、跳过、标记失败等测试结果的清晰描述。
可用字符有:

字符 描述
-f fail失败的用例
-E erro出错的用例
-s skip跳过的用例
-x 标记失败的用例
-X 标记成功的用例
-p pass成功的用例
-P 成功用例并输出结果
-a 所有pP状态的用例

2.6 在python代码中调用执行

pytest.main()

pytest.main('-m login', '--setup-show', 'case4.py')
pytest.main('-x', 'case4.py')

三 、固件fixture

固件(fixture)是一些函数,pytest会在只想测试函数前或之后加载运行。类似unittest中的setup()和teardown()。

3.1 使用固件进行预处理

pytest使用yield关键词将固件分为两部分,yield之前的代码属于预处理,在测试用例开始前运行;yield之后的代码在测试完成后执行。如,测试前需要链接数据库,测试后需要将数据库关闭等。

@pytest.fixture()
def connect():
    print('数据库链接成功')
    yield
    print('数据库关闭成功!')

def select(id):
    d = {'001': '张三'}
    return d[id]

def test_select(connect):

    assert select(id='001') == '张三'

执行结果:


image.png

3.2 作用域

固件使用范围作用域:
在首次请求时创建,根据scope参数声明@pytest.fixture(scope=args),可选参数有:

  • function: 默认范围,在测试结束后自动销毁
  • class:在测试类中执行一次,每个方法都可用
  • module:每个模块执行一次,模块中的函数和方法都可用
  • package:每个包中执行一次,包中的模块都可以
  • session:一次测试只执行一次
import pytest

@pytest.fixture(scope='session')
def order():
    return []

@pytest.fixture
def func(order):
    order.append('function')

@pytest.fixture(scope='class')
def cla(order):
    order.append('class')

@pytest.fixture(scope='module')
def mod(order):
    order.append('module')

@pytest.fixture(scope='package')
def pack(order):
    order.append('package')

@pytest.fixture(scope='session')
def sess(order):
    order.append('session')

class TestClass:
    def test_order(self, func, cla, mod, pack, sess, order):
        assert order == ['session', 'package', 'module', 'function', 'class']

在run.py中执行查看详细过程:

pytest.main(['-x', '--setup-show', 'case3.py'])

结果可见具体的执行顺序和作用域:


image.png

3.3 自动执行

即标记为自动执行的函数,在测试时,就算测试函数没有使用固件也会自动自行被标记的固件。

import pytest

@pytest.fixture(autouse=True)
def get_session():
    return print('获取session')

def test_func():
    """不使用固件"""
    assert 1+1 == 2

测试执行果:


image.png

如上,在case5.py中标记装饰了自动执行函数get_session(),在测试函数test_func()中不使用固件,实际执行测试过程中会先执行get_session()函数。

3.4 重命名

固件默认名称是定义时的函数名,如果不想使用默认名称可通过name对固件进行重命名。

import pytest

@pytest.fixture(name='myname')
def myfuc_001_name():
    return 5

def test_myfuc(myname):
    assert myname == 5

执行结果:


image.png

四、参数化

@pytest.mark.parametrize()
在某些测试用例过程中需要对用例使用参数化,可使用装饰方法设置参数。如判断用户名密码输入长度,
参数化三条数据,即可完成三次用例测试

def login(name, passwd):
    return len(name), len(passwd)

@pytest.mark.slow
@pytest.mark.parametrize('name, passwd', [('user01', '12345678'), ('user1', '12345678'), ('user001', '1234567')])
def test_login(name, passwd):
    user, psd = login(name, passwd)
    assert 6 <= user <= 10, '用户名长度需要为6到10位'
    assert 8 <= psd <= 12, '密码长度为8到12位'

执行结果:
使用“--setup-show”查看到详细执行过程。

image.png

汇总信息

image.png

可看出有两条测试数据不满足条件,所有失败2,成功1

五、测试报告

5.1 生成xml格式测试报告

pytest test.py --junitxml=path

在path中生成测试报告文件

5.2 用pytest-html生成html报告

pip install pytest-html  # 需先安装
pytest test.py --html=report.html

5.3用allure生成测试报告

pip install allure-pytest  # 安装
pytest test.py --alluredir=result_path  # 生成测试结果
allure generate result_path -o report_path --clean  # 在指定目录下生成测试报告
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容