每天努力一点点,坚持下去 ------ 博客首页

Pytest自动化框架

一、Pytest介绍

官网:https://docs.pytest.org/en/7.1.x/

——Pytest是一个测试用例的管理框架,在Unitest基础上做的一个全面的升级.

  1. 集成度更高,而且更加灵活的一个测试框架(测试用例开头不想使用test打头,可进行自定义)
  2. 运行的顺序是自上而下,不是像Unitest是根据字母来进行执行
  3. 全程都是基于指令的形式来运行(通过指令就可以直接生成测试报告)
  4. 可以直接使用Unitest
  5. Pytest默认规则是:寻找当前路径下所有的文件与子文件中以test开头或是结尾的文件夹、文件、函数作为识别对象,所以我们在创建Python模块时以及命名函数时,都需要是【test开头或是结尾】,不然则不会被识别到
  6. Pytest默认不输出任何打印信息,如果要看打印信息,需要在运行时添加 “  -s  ” 的指令
  7. 多条指令一同运行时,需要通过 空格 进行区分,在 main 函数中,是通过 逗号 来分割
  8.  -v 用于详细显示日志信息是否通过
  9.  -rA 测试结果的简单统计
  10. -q 只显示运行结果以及时间
  11. 预置函数: 一般可以通过一个配置文件直接进行管理,配置文件命名一定要是 conftest.py(不能是其他的)用于前期的数据准备,如:可把获取token放入到预置函数中
  12. fixture是pytest中的一大利器

二、Pytest环境部署

pip install pytest

 三、操作使用

import pytest


def test_01():
    print('test-1')

def test_02():
    print('test-2')

if __name__ == "__main__":
    pytest.main()       #不会打印出信息
    pytest.main(['-s']) #可以直接打印出信息

结果:

 我们可以从结果中看出,并没有打印出信息,如果相关查看打印信息,可在【输出结果栏】中的“Terminal"中,进入此路径下,输入命令:pytest -s运行

  • 多个test文件、函数,指定自定义运行

(1)运行指定的 .py模块,可在【输出结果栏】中的“Terminal"中,进入此路径下,输入命令:pytest -s test_case1.py

 

结果:

(2)运行 .py模块下某个函数,可在【输出结果栏】中的“Terminal"中,进入此路径下,输入命令:pytest -s test_case1.py::test_002

 结果:

方法二:代码中指定:pytest.main(['-s','test_case1.py::test_002'])

(3)指定 .py模块下例运行哪几个,以及指定运行顺序,可在【输出结果栏】中的“Terminal"中,进入此路径下,输入命令:pytest -s test_case1.py::test_002 test_case1.py::test_001

 (4)"-V "  打印出用例是否通过结果

import pytest

def test_01():
    print('test-1')

def test_02():
    print('test-2')

if __name__ == "__main__":
    pytest.main(['-v']) # "-V"会打印出用例是否通过结果

 结果:

(5)mian中命令,可放在一起写,如:

import pytest

def test_01():
    print('test-1')

def test_02():
    print('test-2')

if __name__ == "__main__":
    pytest.main(['-sv']) 

 结果:

 (6) -rA 测试结果的简单统计

import pytest

def test_01():
    print('test-1')

def test_02():
    print('test-2')

if __name__ == "__main__":
    pytest.main(['-rA'])

结果:

 

 (7)-q 只显示运行结果以及时间

import pytest

def test_01():
    print('test-1')

def test_02():
    print('test-2')

if __name__ == "__main__":
    pytest.main(['-q'])

 结果:

 

 四、配置文件 conftest.py 中 fixture 的使用:

(1)基本使用

  • conftest.py

import pytest

#预置函数
@pytest.fixture() #装饰器
def keep():
    print('坚持下去!!!')
  • test_case.py中执行代码

import pytest

def test_01(keep):
    print('test-1')

def test_02():
    print('test-2')

if __name__ == "__main__":
    pytest.main(['-s'])

结果:

 我们可以看出,是相当于把 conftest.py 中的预置函数keep传给了 test_case.py中的 test_01函数,执行代码时,首先运行的conftest.py 中的预置函数keep,然后在依次执行的test_case.py中的2个函数。

 (2)断言操作

  • conftest.py

import pytest

@pytest.fixture()
def keep01():
    return 1
  • 在test_case.py中执行代码

import pytest

def test_01(keep01):
    assert keep01 == 1,'失败'
    assert keep01 == 2, '失败'


if __name__ == "__main__":
    pytest.main(['-s'])

结果:

 

 (3)fixture中scope参数定义的4种等级(默认等级是funtion):

  1. session:在本次session级别中只执行一次(说的是运行结果中标题显示的:==test session starts==)
  2. module: 在模块级别中只执行一次,就是说在第一个模块运行一次就可以了,之后的模块在使用,直接取就可以了
  3. class:在类级别中执行一次
  4. funtion:在函数级别中执行,每有一个函数就执行一次
  • conftest.py

import pytest

@pytest.fixture(scope='session') #字符串中还可以是:module、class、funtion
def keep01():
    return 1

五、前置、后置条件

  • setup_functionteardown_function

import pytest

#前置、后置条件
def setup_function():
    print('===setup_function===')

def teardown_function():
    print('***teardown_function***')

def test_01(keep01):
    print('test_01')

if __name__ == "__main__":
    pytest.main(['-s'])

 结果:

 

  • setup_moduleteardown_module

import pytest

#前置、后置条件,函数名字是固定写法,不可以自定义 
def setup_function():
    print('===setup_function===')

def teardown_function():
    print('***teardown_function***')

def setup_module():
    print('。。。setup_module。。。')

def teardown_module():
    print('+++teardown_module+++')

def test_01(keep01):
    print('test_01')

if __name__ == "__main__":
    pytest.main(['-s'])

结果:

 六、类的使用

import pytest

#前置、后置条件
def setup_function():
    print('===setup_function===')

def teardown_function():
    print('***teardown_function***')

def setup_module():
    print('。。。setup_module。。。')

def teardown_module():
    print('+++teardown_module+++')
    
#pytest中class对象的定义:建议以test开头,不需要继承了
class TestDemo:
    def test_d1(self):
        print('类===test_d1===')

    def test_d2(self):
        print('类***test_d2***')


def test_01(keep01):
    print('普通函数test_01')

if __name__ == "__main__":
    pytest.main(['-s'])

结果:

*  我们从结果可以看出setup_function晚于类执行,在类里面是没有执行setup_function的。也就是说在类外边定义的setup_function,只能在类外边起作用,在类里面不起作用。

  • 在类里面使用setupteardown

import pytest

class TestDemo:
    def setup(self):
        print('class setup')

    def teardown(self):
        print('class teardown')

    def test_d1(self):
        print('类===test_d1===')

    def test_d2(self):
        print('类***test_d2***')

if __name__ == "__main__":
    pytest.main(['-s'])

结果:

  •  在类里面使用setupteardown、setup_classteardown_class、setup_metchodteardown_metchod

import pytest

#前置、后置条件
class TestDemo:
    def test_001(self):
        print('类:test_001')
    def setup(self):
        print('类:setup')

    def teardown(self):
        print('类:teardown')

    def setup_class(self):
        print('类===setup_class===')

    def teardown_class(self):
        print('类***teardown_class***')

    def setup_method(self):
        print('类===setup_method===')

    def teardown_method(self):
        print('类***teardown_method***')

if __name__ == "__main__":
    pytest.main(['-s'])

 结果:

  •  在class中 前置、后置 函数的运行顺序:
  1. setup class
  2. setup method
  3. setup
  4. teardown
  5. teardown methon
  6. teardown class

七、pytest中用例的管理手段:mark

——可以通俗的理解为:是一个批量管理工具,标记好了,就可以批量的运行标记为一样的用例了。

  • 可以通过mark装饰器对所有的用例进行标记,不同的标记区分进行管理
  • 在一个py文件还是多个py文件都会生效

(1)运行一种被标记的用例

import pytest

@pytest.mark.webui
def test_01():
    print('web01')

@pytest.mark.webui
def test_02():
    print('web02')

@pytest.mark.app
def test_03():
    print('app01')

@pytest.mark.app
def test_04():
    print('app02')

if __name__ == "__main__":
    pytest.main(['-s','test_case2.py','-m webui']) # -m webui:指定运行是webui的用例

结果:

(2)用例被@pytest.mark标记多个时,运行其中一种被标记的用例

import pytest

@pytest.mark.webui
def test_01():
    print('web01')

@pytest.mark.webui
def test_02():
    print('web02')

@pytest.mark.app
@pytest.mark.temp
def test_03():
    print('app01')

@pytest.mark.app
@pytest.mark.temp
def test_04():
    print('app02')

if __name__ == "__main__":
    pytest.main(['-s','test_case2.py','-m temp']) # -m webui:指定运行是webui的用例

 结果:

 

 (2)同时运行多个不同的被标记种类,-m name1,neme2

pytest.main(['-s','test_case2.py','-m app,webui']) 

八、运行方式:

  1、命令行模式

通过标记表达式执行
pytest -m demo
这条命令会执行被装饰器@pytest.mark.demo装饰的所有测试用例

生成html报告:
pytest -m demo --html=Report/report.html

生成xml报告:
pytest -m demo --junitxml=Report/report.xml

运行指定模块:
pytest -m demo --html=Report/report.html TestCases/test_pytest.py

运行指定测试目录
pytest -m demo --html=Report/report.html TestCases/

通过节点id来运行:
pytest TestCases/test_pytest.py::TestDemo::test_demo01

通过关键字表达式过滤执行
pytest -k "MyClass and not method"
这条命令会匹配文件名、类名、方法名匹配表达式的用例

获取用例执行性能数据
获取最慢的10个用例的执行耗时
pytest --durations=10

2、新建run.py文件运行,代码如下:

pytest.main(["-m","demo","--html=Report/report.html"])

九、pytest框架下的核心配置文件 pytest.ini

     ——配置在工程的根目录下,可以全局生效

import pytest

@pytest.mark.webui
def test_01():
    print('web01')

@pytest.mark.webui
def test_02():
    print('web02')

@pytest.mark.interface
def test_03():
    print('interface01')

@pytest.mark.interface
def test_04():
    print('interface02')

if __name__ == "__main__":
    pytest.main(['-s','test_case2.py'])
  • pytest.ini

——ini 文件的名称必须是pytest.ini,代码中所标黄名称必须这样定义,不可为其他的,否则不起作用

[pytest]

#标签
markers =
   webui: automation for web ui   #冒号后边是标签的注释
   interface: automation for interface
   temp: just for fun

#定义到pytest中,读取哪一些py文件,作为pytest的识别对象
python_files = cema*.py  test*.py

#配置后,会根据指定的 cema*,运行代码时,只会执行cema打头的py文件
#添加多个,逗号隔开,可继续添加


#定义到pytest中,读取哪一些类,作为pytest的识别对象
python_classes = Cema*

#配置后,类名为开头Cema的类,就可以正常被识别到执行了


#指定运行的路径,可以为绝对路径和相对路径
testpaths = cema_case2.py ./ 


#指定运行的函数名
python_functions = vip*  test* #单独配置后,只会运行vip开头的函数;空格间隔,可配置多个


#打印信息显示Passed通过/Failed不通过
log_cli = True
#log_cli 和 -v的区别:
#log_cli会把测试用例的执行显示的更加完善,会显示哪个py文件下哪个类哪个函数,如:test_case.py::TestDemo::test_001 类:test_001
PASSED
# -v 会把【test session starts】打印信息中的信息更加的全面


#配置命令
addopts = -s -v #配置后,工程py文件下都可使用,pytest.main()中可不用在写命令

 十、断言机制

  • conftest.py
import pytest

@pytest.fixture(scope='session') #字符串中还可以是:module、class、funtion
def keep(): # 预先配置下
    return 1
  • pytest.ini
[pytest]

markers =
   webui: automation for web ui
   interface: automation for interface
   temp: just for fun

python_files = cema*.py

#python_classes = Test*

estpaths = cema_case2.py

python_functions = test*

log_cli = True

addopts = -s
  •  cema_case.py——【返回通过】的用例
import pytest

@pytest.mark.webui
def test_01(keep):
    assert keep == 1,"成功"

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

 结果:

  •  cema_case.py——【返回不通过】的用例
import pytest

@pytest.mark.webui
def test_01(keep):
    assert keep == 2,"失败"

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

 结果:

 十一、生成测试报告

——pytest-html 测试报告模块,cmd中安装

pip install pytest-html

安装后,我们配置完了代码运行结束后,想要生成报告,在Terminal(终端)中输入: 

pytest  #输入pytest 后,点击Enter(回车键)

pytest --html=./report/repott.html # =后边是指定路径、文件名称

pytest --html=./report1/repott1.html --self-contained-html #直接生成html文件,不会带有css样式文件,发送给其他人时操作简单
#可放在pytest.ini中的
addopts中

 在指定的目录下会生成:repott.html

 测试报告:

十二、常用断言

pytest 里面断言实际上就是 python 里面的 assert 断言方法,常用的有以下几种:
  • assert xx :判断 xx 为真
  • assert not xx :判断 xx 不为真
  • assert a in b :判断 b 包含 a
  • assert a == b :判断 a 等于 b
  • assert a != b :判断 a 不等于 b

十三、assert小栗子

想在抛出异常之后输出一些提示信息,执行之后就方便查看是什么原因了

import pytest
# 异常信息
def f():
    return 3
def test_function():
    a = f()
    assert a % 2 == 0, "判断 a 为偶数,当前 a 的值为:%s" % a

if __name__ == '__main__':
    pytest.main(['-s','demo.py'])

结果:

 十四、pytest的mark扩展使用

  • @pytest.mark.xfail(reason="")标记为失败,期望值为失败,在函数之上使用,pytest.xfail()预期结果失败,下面的代码不会执行,在函数里使用

  • @pytest.mark.skip()无条件跳过测试用例,pytest.mark.skipif()有条件跳过测试用力

  • @pytest.mark.parametrize(param1,[1,2,3])多组参数单个用例的执行

  • @pytest.mark.run需要插件pytest-ordering # 控制函数执行顺序,@pytest.mark.run(order=1)

  • @pytest.mark.flaky

    最多失败重跑5次,如果失败延迟2秒重跑,可以结合mark标记使用,@pytest.mark.flaky(reruns=5,reruns-delay=2)

    如果是批量执行,命令为:pytest --reruns 5 --reruns-delay2

 十五、pytest常用插件扩展

  • pytest-cov代码覆盖率的检测 pytest --cov=src --cov-report = html

  • pytest -sugar 改变pytest默认外观,增加进度条功能

  • pytest-xdist 并行运行,pytst -n 2

  • pytest-rerunfailures 失败用例重跑

  • pytest-ordering 执行顺序

  • pytest-picked 仅测试上次提交以来已更改的代码

十六、测试用例的执行

目录:

1、主函数模式

(1)运行所有 pytest.main()

(2)指定模块 pytest.main(’[-vs],’,’./testcase/test_day1.py’)

(3)指定目录pytest.main(’[-vs]’),’./testcase’)

(4)通过nodeid指定用例运行:nodeid由模块名,分隔符,类名,方法名,函数名组成

#pytest.main(["-vs"],’./interface_testcase/test_day3.py::test_demo11’)

#pytest.main(["-vs"],’./interface_testcase/test_day3.py::TestLogin::test_01_qianghong1’)

2、命令行模式

(1)运行所有:pytest(2)指定模块 pytest -vs ./testcase/test_day1.py

(3)指定目录 pytest -vs ./testcase

(4)通过nodeid指定用例运行:nodeid由模块名,分隔符,类名,方法名,函数名组成

pytest -vs ./interface_testcase/test_day3.py::test_demo11

pytest -vs ./interface_testcase/test_day3.py::TestLogin::test_01_qianghong1

参数详解:
-s:表示输出调试信息,包括print打印的信息
-v显示更详细的信息
-vs一起使用
-n支持多线程或者分布式运行测试用例
#如 #pytest.main([’-vs’,’./testcase/test_day1.py’,’-n=2’])
# pytest -vs ./testcase/test_day1.py -n 2
#reruns==number 表示失败用例重跑
#pytest -vs ./testcase/test_day2.py --reruns 2
#pytest.main([’–vs’,’./testcase/test_day2.py’,‘reruns=2’]) #失败得的用例重跑两次
#-x表示只要一个用例报错,那么测试停止运行
#–maxfail=2 出现两个失败用例停止
#-k 根据测试用例的部分字符串指定测试用例 pytest -vs test_day2 -k “yang”

pytest--命令行常用参数:http://t.zoukankan.com/zouzou-busy-p-11295444.html


 

posted @ 2021-12-05 11:12  他还在坚持嘛  阅读(1077)  评论(0编辑  收藏  举报