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  # 在指定目录下生成测试报告
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,099评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,828评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,540评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,848评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,971评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,132评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,193评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,934评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,376评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,687评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,846评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,537评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,175评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,887评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,134评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,674评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,741评论 2 351

推荐阅读更多精彩内容