python测试框架 pytest

一:

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

  1. 调试时不想运行这个用例

  2. 标记无法在某些平台上运行的测试功能,

  3. 当前的外部资源不可用时跳过(如果测试数据是从数据库中取到的,连接数据库的功能如果返回结 果未成功就不执行跳过,因为执行也都报错)

  4. 在某些版本中执行,其他版本中跳过。

解决:@pytest.mark.skip跳过这个测试用例,可以加条件skipif,在满足某些条件下才希望通过,否则跳过这个测试。

  1. 功能测试尚未实施或尚未修复的错误,当测试通过时尽管预计会失败(标记为pytest.mark.xfail), 它是一个xpass,将在测试摘要中报告。

  2. 你希望测试由于某种情况而就应该失败

解决:@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








原文地址:https://www.cnblogs.com/1026164853qqcom/p/11280684.html