一:
pytest 是python的一套全功能的测试框架. 优点如下:
1、操作简单,支持多组数据参数化, 支持用例的skip和xfail;
2、支持简单的单元测试和复杂的功能测试,还可以做UI和接口自动化测试;
3、pytest有很多第三方的插件并且支持定义扩展; 如失败重新执行, 断言失败也继续运行,自定义出错停止, 自定义mark标记灵活运行用例....
4、可以很好的集成CI
二:
框架结构:
类似的setup,teardown同样更灵活,
模块级(setup_module/teardown_module)模块始末,全局的
函数级(setup_function/teardown_function)不在类中的函数有用
类级(setup_class/teardown_class)只在类中前后运行一次。
方法级(setup_method/teardown_methond)运行在类中方法始末
session() 跨文件级
三:
Pytest调用方式和用例设计原则:
1. Pytest/py.test(终端,命令行,pycharm都行,可配置pycharm
使用pytest方式执行)
Pytest –v (最高级别信息—verbose -q静默模式)
pytest -v -s -q 文件名 (s是带控制台输出结果,也是输出详细) 2. pytest将在当前目录及其子目录中运行test _ * .py或* _test.py形式
3. 以test_开头的函数,以Test开头的类,以test_开头的方法。所有包package都要有__init__.py文件。
4. Pytest可以执行unittest框架写的用例和方法
四:
Fixture: 对于setup和teardown 有些场景不适用, 同一个类里的用例 有的用例需要执行setup 有些用例不需要;这时候用fixture比较好用.
用法:
1、对于setup,teardown,可以不起这两个名字,所以命名方式灵活。(可独立命名,声明后激活)
2、数据共享。在conftest.py配置里写方法可以实现数据共享,不需要import导入。可以跨文件共享, 使用的文件与conftest文件在同一文件夹下;
3、 scope的层次及神奇的yield组合相当于各种setup 和 teardown的所有文件.
不想原测试方法有任何改动,或全部都自动实现自动应用,没特例,也都不需要返回值时可以选择自动应用
解决: 使用fixture中参数autouse=True实现
步骤:在方法上面加@pytest.fixture(autouse=True) 使用@pytest.mark.usefixtures
步骤:在测试方法上加@pytest.mark.usefixtures("start")
测试离不开数据,为了数据灵活,一般数据都是通过参数传的
解决:fixture通过固定参数request传递;
步骤:在fixture中增加@pytest.fixture(params=[1, 2, 3, 'linda']) ; 在方法参数写request
import pytest
#module模块级,只在运行模块式执行一次。如果不加范围,默认会在该模块中的每一个方法执行前执行;autouse=True设置后实现自动应用,后面模块内所有的函数后面不用加方法名@pytest.fixture(scope="module", autouse=True)def open_browser():
print(" 打开浏览器,打开网站首页")
yield #模块执行完case后 在最后执行一遍teardown操作。
print('执行teardown')
print('最后关闭浏览器')
def test_soso(login): #login函数写在 conftest文件中 @pytest.fixture() def login():
print('case1: 登际后执行搜索')
def test_cakan():
print('case2:不登陆直接操作')
def test_cart(login):
print('case3,登陆后进行购物')
或者使用装饰器方法:不改变原有代码的情况下增加新功能,灵活的让某个方法经过装饰后具备 setup teardown 或其它功能项
@pytest.fixture()
def open_browser():
print("
打开浏览器,打开网站首页")
yield
print('执行teardown')
print('最后关闭浏览器')
@pytest.mark.usefixtures('open_browser')
def test_soso(): #def test_soso(open_browser) 效果是一样的 但是这样会改变原代码结构,其他地方有复用的话就可能会有问题
print('case1: 登际后执行搜索')
Pip install pytest-sugar
pip install pytest-rerunfailures
Pip install pytest-xdist
Pip install pytest-assume
Pip intall pytest-html
test_user_data = ["zhangsan", "lisi"]
@pytest.fixture()
def login_r(request):
user = request.param
print(" 打开首页准备登陆,登陆用户:%s" % user)
return user
@pytest.mark.parametrize('login_r',test_user_data,indirect=True)
def test_login_s(login_r):
print(login_r)
等同于:
@pytest.fixture(params=test_user_data)
def login_r(request):
user = request.param
print(" 打开首页准备登陆,登陆用户:%s" % user)
return user
def test_login_s(login_r):
print(login_r)
多组数据:
test_user_data1 = [{"user": "linda", "password": "888888"},
{"user": "servenruby", "password": "123456"},
{"user": "test01", "password": ""}]
test_user_data2 = [{"q": "中国平安", "count": 3, "page": 1},
{"q": "阿里巴巴", "count": 2, "page": 2},
{"q": "pdd", "count": 3, "page": 1}]
@pytest.fixture(scope="module")
def login_r(request):
# 这是接收传入的参数,接收一个参数
user = request.param['user']
password = request.param['password']
print(" 用户名:%s,密码:%s" % (user, password))
@pytest.fixture(scope="module")
def query_param(request):
# 这是接收传入的参数,接收一第二个参数
q = request.param['q']
count = request.param['count']
page = request.param['page']
print("查询的搜索词:%s" % q)
return request.param
# 这是pytest的参数化数据驱动,indeirect=True 是把login_r当作函数去执行,不写的话会当它是参数
# 从下往上执行
# 两个数据进行组合测试有3*3个测试用例执行(test_user_data1的个数*test_user_data2的个数)
@pytest.mark.parametrize("query_param", test_user_data2, indirect=True)
@pytest.mark.parametrize("login_r", test_user_data1, indirect=True)
def test_login(login_r, query_param):
# 登陆用例
print(login_r)
print(query_param)
七
mark中的skip与xfail
-
调试时不想运行这个用例
-
标记无法在某些平台上运行的测试功能,
-
当前的外部资源不可用时跳过(如果测试数据是从数据库中取到的,连接数据库的功能如果返回结 果未成功就不执行跳过,因为执行也都报错)
-
在某些版本中执行,其他版本中跳过。
解决:@pytest.mark.skip跳过这个测试用例,可以加条件skipif,在满足某些条件下才希望通过,否则跳过这个测试。
-
功能测试尚未实施或尚未修复的错误,当测试通过时尽管预计会失败(标记为pytest.mark.xfail), 它是一个xpass,将在测试摘要中报告。
-
你希望测试由于某种情况而就应该失败
解决:@pytest.mark.xfail
八
场景:只执行符合要求的某一部分用例 可以把一个web项目划分多个模块,然后指定模块名称执行。
App自动化时,如果想Android和IOS公用一套代码时,也可以使用标记功能,标明哪些是IOS 的用例,哪些是Android的,运行代码时指定mark名称运行就可以。
在测试用例方法上加@pytest.mark.webtest 执行:
-s参数: 输出所有测试用的print信息 -m:执行自定义标记的相关用例
pytest -s test_mark_zi_09.py
pytest -s test_mark_zi_09.py -m=webtest #只执行标记为webtest的用例
pytest -s test_mark_zi_09.py -m apptest
pytest -s test_mark_zi_09.py -m "not ios" #不执行标记为 ios的用例
九
只执行符合要求的某一部分用例,通过类与方法的命名实现。通常编写测试方法时
方法一:直接输入文件名,类名
pytest test_class_01.py
pytest -v -s test_class_01.py
pytest -v test_class_01.py::TestClass
pytest -v test_class_01.py::TestClass::test_one #只执行该测试类中的test_one
方法二 :使用-k
pytest -k "TestClass and test_one" #只执行该测试类中的test_one
pytest -k "TestClass or test_one"
TestClass是类名,and是运算符,还可以是and not...,test_one是方法名中含有的信息。
十
正常全部执行完成后才能停止,如果想遇到错误时停止测试:- x;也可以当用例错误个数n达到指定数量时,停止测试:- - maxfail=n
pytest -x -v -s test_class_01.py
pytest -x -v -s test_class_01.py - -maxfail=2 #错误数达到2个时停止测试
十一
测试失败后要重新运行n次,要在重新运行之间添加延迟时间, 间隔n秒再运行。
执行:
安装:pip install pytest-rerunfailures
pytest --reruns 3 -v -s test_reruns_10.py pytest -v - -reruns 5 --reruns-delay 1
十二
一个方法中写多条断言,通常第一条过去,下面就不执行了。我们想报错也都执行一下。
执行:安装pip3 install pytest-assume
pytest.assume(1==4) #注意此处用的不是assert 断言
pytest.assume(2==4)
十三
测试用例1000条,一个用例执行1钟,一个测试人员执行需要1000分 钟。通常我们会用人力成本换取时间成本,加几个人一起执行,时间就会 缩短。如果10人一起执行只需要100分钟,这就是一种并行测试,分布式场景。
解决:pytest分布式执行插件:pytest-xdist,多个CPU或主机执行 前提:用例之间都是独立的,没有先后顺序,随机都能执行,可重复运行
不影响其他用例。
安装:Pip3 install pytest-xdist
多个CPU并行执行用例,直接加-n 3是并行数量:pytest -n 3 在多个终端下一起执行
Pytest -v -s --html=report.html #生成html报告 利用 pytest-html