Pytest 入门学习
pytest是一个非常成熟的全功能的Python测试框架,主要特点有以下几点:
简单灵活,容易上手;
支持参数化;
能够支持简单的单元测试和复杂的功能测试,还可以用来做selenium/appnium等自动化测试、接口自动化测试(pytest+requests);
pytest具有很多第三方插件,并且可以自定义扩展,比较好用的如pytest-selenium(集成selenium)、pytest-html(完美html测试报告生成)、pytest-rerunfailures(失败case重复执行)、pytest-xdist(多CPU分发)等;
测试用例的skip和xfail处理;
可以很好的和jenkins集成;
一、安装
安装
pip install -U pytest
检查是否安装成功
pytest --version
This is pytest version 4.5.0, imported from c:\python27\lib\site-packages\pytest.pyc
查看帮助命令
pytest -h
二、快速入门
创建一个test_sample.py
def func(x):
return x + 1
class TestCase():
def test_answer(self):
assert func(3) == 5
运行用例
命令行通过以下命令运行:
运行所有文件:pytest
运行单个文件:pytest test_sample.py
运行结果:
结论:
从上面看出,当用例运行失败时,会在控制台打印具体的错误信息,包含用例信息及具体错误代码和预期结果。错误信息一目了然。
非命令行运行
代码右键运行或直接从pytest.main()运行,返回结果如下:
三、pytest用例识别
pytest会找当前以及递查找子文件夹下面所有的test_.py或_test.py的文件,把其当作测试文件
在这些文件里,pytest会收集下面的一些函数或方法,当作测试用例:
1、不在类定义中的以test_开头的函数或方法
2、在以Test开头的类中(不能包含init方法),以test_开头的方法
3、pytest也支持unittest模式的用例定义
四、运行用例
1、运行单个用例
使用命令,pytest+文件名 运行,如运行test_class.py
pytest test_class.py
2、运行多个用例
运行多个用例,使用命令pytest
3、运行指定用例
3.1通过匹配关键字,运行用例
使用命令,pytest -k keyword 运行指定关键字用例
3.2通过用例标记,运行用例
1)用例标记:pytest允许为测试用例添加属性通过 pytest markers,@pytest.mark,定义方法: @pytest.mark.<name>
2)运行标记的用例,如运行被标记falled的用例:pytest -m falled
结论:
由上图可看出,文件中被标记为falled的用例被执行。
3、运行文件夹下的用例
使用命令,pytest + 文件夹,可运行该文件夹下的用例,如运行testing文件夹下的用例
结论:
通过 pytest+文件名/ 命令,可运行该文件夹下的所有用例。
4、运行模块下类内指定的用例
使用命令,pytest+文件名+类名+方法名,指定运行测试用例,格式为:pytest::test_.py::Test::test
结论:
由上图可以看到,使用命令 pytest::test_sample.py::TestCase::test01,可以运行test_sample.py文件中TestCase类下的test01方法。
五、运行用例,显示指定结果信息
运行测试用例,显示总的结果信息,使用命令 pytest -ra
运行测试用例,显示通过的结果信息,使用命令 pytest -rp
运行测试用例,显示错误结果信息警告信息,使用命令 pytest -rE
运行测试用例,显示跳过结果信息,使用命令 pytest -rs
======================== short test summary info ========================
SKIPPED [1] test_skip_case.py:4: unconditional skip
SKIPPED [1] test_skip_case.py:9: unconditional skip
六、环境准备和环境清理
pytest使用Fixture功能为测试用例进行环境准备和环境清理,相当于unittest中的setup\teardown,
setupClass\teardownClass。同时对于传统的setup/teardown函数做了显著的改进:
1)测试fixture有明确的名称,通过在函数/模块/类或者整个项目中激活来使用。
2)测试fixture是模块化的实现,使用fixture名即可触发特定的fixture,fixture可以在其他fixture中进行使用
3)测试fixture不仅可以进行简单的单元测试,也可以进行复杂的功能测试。可以根据配置和组件的选项进行参数化定制测试,或者跨函数/类/模块或者整个测试过程进行测试。
1、环境准备
使用@pytest.fixture装饰器来设置一个函数为测试fixture,后面的测试函数调用到fixture名称时,pytest会检测到该fixture并加载其中的数据作为参数传入。定义fixture的方式:在单个测试文件中定义
、多个文件共享fixture、通过设置fixture中的scope指定作用范围定义,格式为@pytest.fixture(scope=session/module/class/function),其中作用范围大小为:session>module>class>function。具体实现详见例子~
1.1单个函数定义fixture
1)创建一个文件test_fixture01.py
@pytest.fixture() # 定义some_data函数为测试fixture
def some_data():
return 42
def test_some_data(some_data): # 测试函数调用some_data,并做断言
assert some_data == 42
2)调用并执行以上文件:执行命令 pytest --setup-show test_fixture01.py
E:\自动化\pytest框架学习\learn_fixture>pytest --setup-show test_fixture01.py
========================test session starts ============================
platform win32 -- Python 3.7.2, pytest-5.2.2, py-1.8.0, pluggy-0.13.0
rootdir: E:\乐乎自动化\pytest框架学习\learn_fixture
plugins: forked-1.1.1, html-2.0.0, metadata-1.8.0, xdist-1.30.0
collected 1 item
test_fixture01.py
SETUP F some_data
test_fixture01.py::test_some_data (fixtures used: some_data).
TEARDOWN F some_data
========================== 1 passed in 0.12s ==========================
结论:
由上执行结果可以看到,在执行test_some_data用例时,test_some_data调用了测试fixture some_data,并在用例执行前后进行了setup和teardown操作。
1.2多个文件共享fixture
在测试用例实现过程中,当需要使用来自多个文件的fixture时,可以将这些fixture函数写到conftest.py文件中。使用时,不需要导入fixture函数,pytest会自动检索。
fixture函数的使用从测试类开始,到测试模块,然后是conftest.py文件,然后是内置的插件和第三方插件。也可以使用conftest为本地目录实现插件。
1)修改test_fixture01.py文件,分别改为两个文件,将测试fixture函数存入到conftest.py文件中,测试函数另起一个文件为test_fixture02.py
content of conftest.py
@pytest.fixture() # 定义some_data函数为测试fixture
def some_data():
return 42
content of test_fixture02.py
def test_some_data(some_data): # 测试函数调用some_data,并做断言
assert some_data == 42
2)执行多个文件:执行命令 pytest --setup-show test_fixture01.py test_fixture02.py
结论:
由上图执行结果可看到,当将测试fixture函数放在conftest.py文件中,测试用例调用时,可实现多个文件共享fixture函数。
1.3通过设置fixture中的scope指定作用范围
1)指定@pytest.fixture(scope="function"),执行文件时,所有测试文件的每个function函数执行时都会调用fixture函数一次。【不指定情况下,默认为funtion】
结论:
当设置scope="funtion"时,每个用例执行前后都会调用一次function。
2)指定@pytest.fixture(scope="class"),执行文件时,每个类执行前后只调用测试fixture一次。
结论:
当设置sope="class"时,在调用类时,只调用一次funtion。
3)指定@pytest.fixture(scope="module"),执行文件时,只调用fixture一次
创建一个test_fixture04.py包含两个类及一个测试函数,并执行文件:
结论:
从上图可以看到,在执行测试文件时,执行前和执行后只进行了一次funtion的调用。
4)指定@pytest.fixture(scope="session"),执行一个会话时,只调用fixture一次。
注:一个会话是一次文件的调用,可以同时调用多个文件。
结论:
当设置scope="session"时,只是在整个会话开始前和结束后调用一次function。
2、环境清理
pytest支持在fixture退出作用域的时候执行相关的清理/结束代码。使用yield而不是return关键字的时候,yield后面的语句将会在fixture退出作用域的时候被调用来清理测试用例。
1)创建一个文件test_fixture06.py,包含test fixture和test function两部分
import pytest
@pytest.fixture(scope=**"module"**)
def something():
print(**"**\n**哈哈哈哈****"**) # setup部分数据
yield # 使用yield后面的语句将会在fixture退出作用域的时候被调用来清理测试用例
print(**"****结束了?****"**)
def test_smtp(something):
print(**"****测试中。。。。****"**)
2)运行文件:pytest -s test_fixture06.py
七、设置断言
断言,每个测试用例都需要断言。unittest中使用unittest自带的断言机制,与unittest不同的时,pytest使用的是python自带的assert关键字来进行断言。
assert关键字后面可以接一个表达式,只要表达式的最终结果为True,那么断言通过,用例执行成功,否则用例执行失败。断言失败有详细的用例失败描述。
1、断言
代码:
def f():
return 3
def test_function():
assert f() == 4
执行结果:
总结:
从执行结果可以看到,用例断言失败了,且有详细的失败信息描述,可以一眼看出是哪儿出了问题。
2、断言异常抛出
八、用例参数化
参数化测试是用于一些测试流程一样,测试数据不同的测试用例,以此来简化代码。
pytest支持使用fixture中通过param="参数集"初始化需要传入的参数,再将fixture作为参数传入用例中实现参数化。也可通过使用parametrize支持多个参数化传入。
1、参数化fixture(参数化1个)
校验用户弱密码~
users文件:
[
{"name":"jack","password":"Iloverose"},
{"name":"rose","password":"Ilovejack"},
{"name":"tom","password":"password123"},
{"name":"mike","password":"password"},
{"name":"james","password":"AGoodPasswordWordShouldBeLongEnough"}
]
用例文件:
'''用例需求:
校验多个用户、密码是否存在弱密码。
用例设计:
使用参数化形式,一个测试方法传入user集参数,使用例一次性校验所有用户
是否存在弱密码。
'''
import pytest
import json
users = json.loads(open(**'./user.json'**, **'r'**).read()) # 从user.json文件中读取出用户数据
class TestUserPasswordWithParam():
@pytest.fixture(params=users) # 将user作为参数传入,并返回user中的每个用户信息。再将此fixture作为参数传入到用例中数据
def user(self, request):
user_info = request.param # 使用request.param从users中一个一个地拿出数据并返回
return user_info
def test_user_password(self, user):
password = user[**'password'**]
assert len(password) >= 6
msg = **"****用户****%s****存在弱密码****"** % (user[**'name'**])
assert password != **'password'**, msg
assert password != **'password123'**, msg
测试结果:
总结:
从上面运行结果可以看到,用例一共运行了5次。
2、parametrize多参数(参数化多个)
此功能,类似unittest中的ddt数据驱动模式
@pytest.mark.parametrize 装饰器可以让我们每次参数化fixture的时候传入多个项目
校验算数加法:
import pytest
@pytest.mark.parametrize(
**"a,b,c"**, # 参数名---多个参数
[(1, 2, 3), # 参数值---参数值与参数对应,可传入多个需要校验的参数值
(3, 4, 7),
(5, 6, 11)]
)
def test_add(a, b, c):
assert a+b == c
测试结果:
总结:
加法校验case中,传入了3组参数值,实际用例也运行了3次,每次取不同的数据。
九、跳过用例
当遇到不想运行的用例,可对该用例标记为跳过,在执行测试时,会自动跳转被标记的用例。使用
@pytest.mark.skip 标记跳过用例,使用@pytest.mark.xfail 标记用例时,则用例失败执行失败后,无任何错误信息。
下面的详细的用例代码:
test_skip_case.py
import pytest
@pytest.mark.skip # skip标记跳过用例,不执行
def test_add_1():
assert 100 + 200 == 400, **"failed"**
@pytest.mark.skip
def test_add_2():
assert 100 + 200 == 300, **"failed"**
@pytest.mark.xfail # xfail标记执行用例,但是不统计执行结果
def test_add_3():
assert 15 + 13 == 28, **"passed"**
@pytest.mark.xfail
def test_add_4():
assert 15 + 13 == 100, **"failed"**
def test_add_5():
assert 3 + 2 == 5, **"passed"**
def test_add_6():
assert 3 + 2 == 6, **"failed"**
执行用例:pytest -v test_skip_case.py
[图片上传失败...(image-f7e8d5-1573551303032)]
可以看到,测试用例文件中,前两个用例未执行,第三、四个执行了,且第三个执行失败了,未收集失败信息。第五、第六个正常执行,第六个执行失败,显示具体的失败追踪信息。
十、生成测试报告
1、生成HTML格式的测试报告
生成html格式的测试报告,需要用例到插件pytest-html。具体步骤如下:
1)安装插件:pip install pytest-html
2)执行测试并生成测试报告:pytest -v -s --html=report.html
2、生成XML格式的测试报告
生成junit格式的xml报告,在命令行中加入--junit-xml=path 参数就可以了.
pytest -v --junit-xml=report.xml
十一、常见命令行参数
参考:
https://blog.csdn.net/lb245557472/article/details/90341297)
http://www.testclass.net/pytest
https://blog.csdn.net/crazyskady
http://pytest.org/en/latest/contents.html
https://www.bilibili.com/video/av59183665/