pytest+allure基础知识

介绍

  • pytest是基于unittest开发的另一款更高级更好用的单元测试框架
  • 支持参数化
  • 执行测试过程中可以将某些测试跳过(skip),或者对某些预期失败的case标记成失败
  • 支持运行由 nose, unittest 编写的测试 case
  • 方便的和持续集成工具 jenkins 集成
  • 具有很多第三方插件,并且可以自定义扩展
  • 可支持执行部分用例
  • 支持失败重跑功能

安装

  • pytest是第三方库,需要安装后使用
  • 在命令行输入以下任意命令即可安装,使用第三方镜像源可加快下载速度
# 豆瓣源
pip install pytest -U -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
# 清华源
pip install pytest -U -i https://pypi.tuna.tsinghua.edu.cn/simple/ --trusted-host pypi.tuna.tsinghua.edu.cn

命名规范

  • 用例文件应当以 test_*.py 进行命名,或者以 *_test.py命名
  • 类必须以 Test 开头,且类当中不能有__init__方法
  • 方法或函数必须以 test_ 开头
  • 可以在项目根目录建pytest.ini文件,在里面自定义用例匹配规则
# 用例匹配规则
[pytest]
python_files = test_*.py *_test.py
python_classes = Test*
python_functions = test_*

断言

  • 断言必须使用assert,语法:assert 表达式
  • 测试的结果, .表示成功 F表示失败,如果测试失败,会显示具体的语句
assert 1==2 #判断等式两边是否相等
assert 200 #判断某个语句是否为真
assert 10 in [10,20] #判断某个值是否属于某个对象
assert not True #判断某个语句是否不为真
assert 1!=2 #判断某个值是否不等于另一个值

初始化和清除

  • 模块级别的初始化和清除
def setup_module():
    print('\n *** 模块初始化开始 ***')
    pass # 这里写初始化代码
    print('\n *** 模块初始化结束 ***')
def teardown_module():
    print('\n *** 模块清除开始 ***')
    pass # 这里写清除代码
    print('\n *** 模块清除结束 ***')
  • 类级别的初始化和清除
class Test_A:    
    @classmethod
    def setup_class(cls):
        print(f'\n === {cls.__name__}类初始化 ===')
    @classmethod
    def teardown_class(cls):
        print(f'\n === {cls.__name__}类清除 ===')
  • 方法级别的初始化和清除(类中所有方法都会调用)
    def setup_method(self):
        print('\n --- 类方法初始化  ---')
    def teardown_method(self):
        print('\n --- 类方法清除 ---')
  • 方法级别的初始化和清除(类中指定方法调用)
    # 在类外面自定义一个方法,名字随意,但是要带上pytest的装饰器
    @pytest.fixture(scope='function')
    def custom_setup():
        print(f'\n --- 自定义方法初始化  ---')
        pass # 在这写要初始化的代码
        yield custom_setup # 初始化函数名(不要带括号)
        custom_teardown() # 清除函数
        
    # 同时要自定义一个清除函数,名字随意
    def custom_teardown():
        print(f'\n --- 自定义方法清除 ---')
        pass # 在这写清除的代码
        
    # 如果有个方法需要调用自定义的初始化和清除函数,只需要把初始化函数传入即可
    def test_abc(custom_setup):
        pass 
  • 执行顺序(以类中的一个方法为例):模块初始化→类初始化→类方法初始化→自定义方法初始化→自定义方法清除→类方法清除→类清除→模块清除
  • 类方法的初始化和清除会作用于类里的每一个函数
  • 自定义方法的初始化和清除只会对传入该方法名的函数生效

参数化

  • 使用@pytest.mark.parametrize(参数名, 参数值列表)
# 传入多个参数时,参数值列表中,每一组数据的类型都是元组
@pytest.mark.parametrize("val_a,val_b", [(1, 2), (3, 4), (5, 6)])
def test_parametrize_1(val_a,val_b):
    print(f"变量val_a的值:{val_a},变量val_b的值:{val_b}")
    
# 可以使用多个参数化装饰器,测试数据为不同数据的笛卡尔积,实际会生成m*n条用例(下面的代码生成3*4=12条用例)
@pytest.mark.parametrize('a', [1,2,3])
@pytest.mark.parametrize('b', [4,5,6,7])
def test_parametrize_2(a, b):
    print(f'笛卡尔积 测试数据为:{a},{b}')

mark标记用例

  • pytest支持案例标记的功能,可以给每个用例加上自定义标记,在执行测试案例时通过标记筛选要执行的案例
  • 使用标记方法
    1. pytst.ini 中添加标记名称
    2. 在需要标记的函数上使用装饰器 @pytest.mark.标记名
  • pytst.ini :pytest的配置文件
# 在pytst.ini文件的标记名列表中添加自定义的标记
[pytest]
markers =
    smoke
    test
    hello

# 在需要标记的函数上增加装饰器(以smoke,hello为例)
@pytest.mark.smoke
def test_001(self):
    print('我是一个冒烟测试用例')

@pytest.mark.hello
def test_002(self):
    print('我是一个属于hello标记的测试用例')

执行用例

  • '-k 标记名' ==>可选择执行该标记所包含的用例
  • '-s' ==>在控制台输出执行过程中的print内容
  • '-q' ==>打印用例执行的简略过程
  • '-v' ==>打印用例执行的详细过程
  • '-x' ==>用例运行失败则立即停止执行
  • '--maxfail=num' ==>用例运行时允许的最大失败次数,超过num次则立即停止
  • '-l' ==>用例运行失败时,打印相关的局部变量
  • 不输入参数默认执行该当前目录及子目录中的所有测试用例
  • pytest执行案例时使用pytest.main()函数,参数传入一个列表
# 执行所有标记为smoke的用例,显示详细过程,并打印print输出
pytest.main(['-k','smoke','-sv'])
  • 如果执行过程中出现了Module already imported so cannot be rewritten的警告,那么可以用子进程的方式来执行用例,就不会弹出这种警告了
# 导入子进程模块,避免运行时弹出警告
import subprocess
# 列表中第一个元素为pytest,后面的元素可以输入各种参数
subprocess.call(['pytest', '-k smoke', '-s'])
# 运行指定目录下用例
subprocess.call(['pytest', './test'])
# 运行指定模块
subprocess.call(['pytest', './test/test_demo.py'])
# 运行模块中的指定用例
subprocess.call(['pytest', './test/test_demo.py::test_001'])
# 运行类中的指定用例
subprocess.call(['pytest', './test/test_demo.py::TestDemo::test_001'])
# 运行test_demo.py模块下名称包含hello的用例
subprocess.call(['pytest', '-k hello','./test/test_demo.py'])
# 运行TestDemo类中名称包含aa的用例
subprocess.call(['pytest', '-k aa','./test/test_demo.py::TestDemo'])

allure安装

  • 下载allure:https://github.com/allure-framework/allure2/releases
  • 解压allure.zip到一个文件目录中
  • 将allure报告安装目录\bin所在的路径添加环境变量path中
  • 命令行输入pip install allure-pytest -i http://pypi.douban.com/simple/
  • 在命令行中输入allure,如果能看到命令就是已经配置完成

allure装饰器

  • 根据实际需要对用例使用allure装饰器,可以提升allure报告的可读性
  • 几种常用的装饰器:
@allure.epic('层级1')
@allure.feature('层级2') 
@allure.story('层级3') 
@allure.title('用例标题') 
@allure.description('用例描述')

allure报告

  • allure生成报告分为两步:
    1. 第一步先执行用例并生成测试数据(一大堆json文件)
    2. 根据某次执行用例后生成的测试数据,通过allure generate命令来生成测试报告(包含了index.html,可在浏览器打开查看)
  • 生成allure测试数据
# 这里我是在每次执行的时候,将当天的测试数据和测试报告按日期+时间的双层目录来存放
date = time.strftime('%Y%m%d', time.localtime())  # 当前日期
time = time.strftime('%H%M%S', time.localtime())  # 当前时间
# allure命令要在命令行使用,在python中可通过os.system来调用
os.system(f'pytest --alluredir ./result/{date}/{time}/')  # 生成测试结果
  • 生成allure测试报告
# 根据测试结果生成测试报告
# 加上--clean参数可以先清除数据,但由于我是按时间存放的,所以每次生成的目录都在不同位置,这里就不clean了,自己调试代码测试的时候可以用上clean
os.system(f'allure generate ./result/{date}/{time}/ -o ./report/{date}/{time}/')
  • 手动打开测试报告:在生成的测试报告文件夹里有index.html文件,直接在浏览器打开即可查看
  • 也可以起一个服务,会自动打开allure测试报告
  • 注意:使用这个命令后会在本地起一个服务,如果打开的网页是空白的(可能存在网络限制等各种原因),那么可以将ip换成 127.0.0.1:(启动服务后命令行显示的端口号) 试下能不能正常访问
# 自动打开测试报告,将启动后的服务ip换成 127.0.0.1
os.system(f'allure serve ./result/{date}/{time}/')
上一篇:Mac下 allure的下载与配置


下一篇:pytest+yaml+allure接口自动化测试框架02.封装日志与缓存