pytest
Pytest is a framework that makes building simple and scalable tests easy. Tests are expressive and readable—no boilerplate code required. Get started in minutes with a small unit test or complex functional test for your application or library.
1.1 pytest初识
pytest是一个非常成熟的全功能的Python测试框架,主要有以下几个特点
- 简单灵活,容易上手
- 支持参数化
- 能够支持简单的单元测试和复杂的功能测试
- pytest具有很多第三方插件,并且可以自定义扩展(pytest-selenium、pytest-html、pytest-rerunfailures、pytest-xdist、allure)
- 测试用例的skip和xfail处理
- 可以很好的和jenkins集成
1.2 pytest安装
pip install -U pytest
pytest --version # 会展示当前已安装版本
1.3 pytest约束
- 所有的单测文件名都需要满足test_.py格式或_test.py格式
- 在单测文件中,测试类以Test开头,并且不能带有 init 方法(注意:- 定义class时,需要以T开头,不然pytest是不会去运行该class的)
- 在单测类中,可以包含一个或多个test_开头的函数。
- 在执行pytest命令时,会自动从当前目录及子目录中寻找符合上述约束的测试函数来执行
1.4 pytest参数
- -m : 标记markers用于标记测试 @pytest.mark.xxxx
- -s : 标准输出,输出程序中 print
- -k :筛选用例 使用表达式来指定希望运行的测试用例
- -x :遇失败停止 测试在遇到第一次失败就停止
- -q :输出简化的信息
- -v :输出结果详细
- -r :生成简略的指定需求的报告
- -l :展示运行过程中的全局变量和局部变量
1.5 pytest setup、teardown
1.5.1 函数级别
- 函数级的(setup_function、teardown_function)只对函数用例生效,而且不在类中使用
#!/usr/bin/env/python
# -*-coding:utf-8-*-
import pytest
def setup_function():
print "每个方法之前执行"
def teardown_function():
print "每个方法之后执行"
def add(a,b):
return a+b
def test_add():
print "正在执行"
assert add(3,4) == 7
if __name__=="__main__":
pytest.main(["-s","test_function.py"])
每个方法之前执行
正在执行
.每个方法之后执行
============================== 1 passed in 0.03s ==============================
1.5.2 类级别
- 类级的(setup_class(self)、teardown_class(self) 只运行该类用例生效
#!/usr/bin/env/python
# -*-coding:utf-8-*-
import pytest
class TestClass(object):
def setup_class(self):
print ("每个类之前执行一次")
def teardown_class(self):
print ("每个类之后执行一次")
def add(self,a,b):
print ("这是加法运算")
return a+b
def test_add(self):
print ("正在执行")
assert self.add(3, 4) == 7
if __name__=="__main__":
pytest.main(["-s","test_class.py"])
test_class.py
每个类之前执行一次
正在执行
这是加法运算
.每个类之后执行一次
============================== 1 passed in 0.06s ==============================
1.6 pytest fixture
1.6.1 scope
- fixture里面有个scope参数可以控制fixture的作用范围:
session>module>class>function
-function:每一个函数或方法都会调用
-class:每一个类调用一次,一个类中可以有多个方法
-module:每一个.py文件调用一次,该文件内又有多个function和class
-session:是多个文件调用一次,可以跨.py文件调用,每个.py文件就是module
1.6.2 params
- @pytest.fixture有一个params参数,接受一个列表,列表中每个数据都可以作为用例的输入
#!/usr/bin/env/python
# -*-coding:utf-8-*-
import pytest
@pytest.fixture(scope="module",params=["params1","params2","params3"])
def params(request):
return request.param
def test_params(params):
print(params)
assert "params" in params
if __name__ == "__main__":
pytest.main(["-s", "test_fixtrue.py"])
test_fixtrue.py::test_params[params1] params1
PASSED
test_fixtrue.py::test_params[params2] params2
PASSED
test_fixtrue.py::test_params[params3] params3
PASSED
============================== 3 passed in 0.06s ==============================
1.6.3 ids
- 字符串id的列表,每个id对应于参数,是测试id的一部分,如果没有提供id,它们将从参数自动生成标识[测试用例编号]
#!/usr/bin/env/python
# -*-coding:utf-8-*-
import pytest
@pytest.fixture(scope="module",params=["params1","params2","params3"],ids=["num1","num2","num3"])
def params(request):
return request.param
def test_params(params):
print(params)
assert "params" in params
if __name__ == "__main__":
pytest.main(["-s", "test_fixtrue.py"])
test_fixtrue.py::test_params[num1] params1
PASSED
test_fixtrue.py::test_params[num2] params2
PASSED
test_fixtrue.py::test_params[num3] params3
PASSED
============================== 3 passed in 0.06s ==============================
1.6.4 autouse
- True,则为所有测试激活fixture func可以看到它
- False则显示需要参考来激活fixture
1.6.5 name
- fixture的名称,这默认为装饰函数的名称。如果fixture在定义它的统一模块中使用,夹具的功能名称将被请求夹具的功能arg遮蔽,解决这个问题的一种方法时将装饰函数命令"fixture_“然后使用”@pytest.fixture(name=’’)"。
1.7 pytest 装饰器
1.7.1 pytest.mark.skip
- 可以用于测试函数外,跳过该测试用例
@pytest.mark.skip(reason='Environment not supported')
def test():
pass
1.7.2 pytest.mark.skipif
- 可以用于测试函数外,有条件地跳过测试用例
@pytest.mark.skipif(condition='1<2',reason='Environment not supported')
def test():
pass
1.7.3 pytest.skip()
- 可以用于测试函数里,跳过测试用例
def test():
if 1 < 2:
pytest.skip('跳过该用例')
pass
1.7.4 pytest.mark.xfail
- 使用xfail标记测试用例预期失败,如果测试用例运行实际成功则显示XPASS,实际失败则显示XFAIL,xfail标记并不影响用例的运行
@pytest.mark.xfail(condition='1==1', reason="feature not implemented")
def test_1():
pass
@pytest.mark.xfail(condition='1==1', reason="feature not implemented")
def test_2():
assert 1 == 2
执行结果:
xfail_test.py::test_1 XPASS
xfail_test.py::test_2 XFAIL
1.7.5 pytest.mark.XXX
- 自定义测试集
@pytest.mark.function
def test():
pass
1.8 pytest 配置文件
1.8.1 pytest.ini
- pytest.ini 可以修改 pytest 的默认行为,需要在项目内加载生效,pytest.ini 不能使用任何中文符号,包括汉字、空格、引号、冒号等
# pytest.ini
[pytest]
markers =
demo : marks tests as demo
smoke: marks tests as smoke
test : marks tests as test # mark标记
testpaths = test_path # 指定测试,目录
python_files = test_* *_test check_* # 修改测试用例发现规则
python_classes = Test* Check*
python_functions = test_* check_*
log_cli = 1 # 控制台实施输出记录
addopts = -rsxX -l -strict --tb=short # 命令行参数设置为默认
1.8.2 conftest.py
-
优点
1、可以跨.py文件调用,有多个.py文件调用时,可让conftest.py只调用了一次fixture,或调用多次fixture
2、conftest.py与运行的用例要在同一个pakage下,并且有__init__.py文件
3、不需要import导入 conftest.py,pytest用例会自动识别该文件,放到项目的根目录下就可以全局目录调用了,如果放到某个package下,那就在改package内有效,可有多个conftest.py
4、conftest.py配置脚本名称是固定的,不能改名称
5、conftest.py文件不能被其他文件导入
6、所有同目录测试文件运行前都会执行conftest.py文件
-
应用场景
1、每个接口需共用到的token
2、每个接口需共用到的测试用例数据
3、每个接口需共用到的配置信息
4、常和 fixture 一起使用
1.9 pytest-html
1.9.1 安装
pip install pytest-html
1.9.2 命令
pytest --html=report.html # 当前路径
pytest --html=./report/report.html # 指定目录
pytest --html=report.html --self-contained-html # 带CSS