在Python语言系中,有很多可用的自动化测试框架,比如早期大多数人会选用unittest+HTMLTestRunner、Nose等,最近几年比较常用的有Robot Framework,Robot Framework它是Python下一款非常通用的测试框架,采用扩展插件的机制可以帮助我们实现几乎任何类型的自动化测试工作,如接口自动化测试、App自动化测试、Web UI自动化测试等。
今天本文重点介绍在Python语言下,另外一款通用的测试框架Pytest,虽说作为Robot Framework框架一书的作者去介绍Pytest,貌似不太合理,但框架技术本是一家,能快速解决实际问题的框架就是好框架,在年初的时候,也发表过一篇关于Robot Framework与Pytest框架选择的一些建议:聊一聊:Robot Framework被误会多年的秘密,感兴趣的读者可以看看。
一句话总结:Pytest核心思路和Robot Framework大体一样,可以通过插件扩展的形式,来满足不同场景下的自动化测试需求。
1. Pytest介绍
Pytest是一个非常成熟的全功能的Python测试框架,与python自带的unittest测试框架类似,但是比unittest框架使用起来更简洁,功能更强大。
提供完善的在线文档,并有着大量的第三方插件和内置帮助,适用于许多小型或大型项目。适合简单的单元测试到复杂的功能测试。还可以执行nose,unittest和doctest风格的测试用例。支持良好的集成实践, 支持扩展的 xUnit 风格 setup,支持非 Python 测试。支持生成测试覆盖率报告,支持 PEP8 兼容的编码风格。
2. Pytest安装及基本使用
Pytest安装非常简单,可以通过 pip 命令直接在线安装:
pipinstall-Upytest
Pytest 官方文档:https://docs.pytest.org/en/latest/
安装好之后,调用 pytest测试脚本方式:
1、py.test:
Pytest 提供直接调用的命令行工具,即 py.test,最新版本 pytest 和 py.test 两个命令行工具都可用
2、python -m pytest:
效果和 py.test 一样, 这种调用方式在多 Python 版本测试的时候是有用的, 例如测试 Python3:
python3-mpytest[...]
基本语法:
usage:py.test[options][file_or_dir][file_or_dir][...]
部分参数介绍:
py.test--version查看版本 py.test--fixtures,--funcargs查看可用的fixtures pytest--markers查看可用的markers py.test-h,--help命令行和配置文件帮助 #失败后停止 py.test-x首次失败后停止执行 py.test--maxfail=2两次失败之后停止执行 #调试输出 py.test-l,--showlocals在traceback中显示本地变量 py.test-q,--quiet静默模式输出 py.test-v,--verbose输出更详细的信息 py.test-s捕获输出,例如显示print函数的输出 py.test-rchar显示指定测试类型的额外摘要信息 py.test--tb=style错误信息输出格式 -long默认的traceback信息格式化形式 -native标准库格式化形式 -short更短的格式 -line每个错误一行 #运行指定marker的测试 pytest-mMARKEXPR #运行匹配的测试 py.test-kstringexpr #只收集并显示可用的测试用例,但不运行测试用例 py.test--collect-only #失败时调用PDB py.test--pdb
3.Pytest用例执行
3.1 用例查找规则
如果不带参数运行pytest,那么其先从配置文件(pytest.ini,tox.ini,setup.cfg)中查找配置项testpaths指定的路径中的test case,如果没有则从当前目录开始查找,否则,命令行参数就用于目录、文件查找。查找的规则如下:
查找指定目录中以 test 开头的目录
递归遍历目录,除非目录指定了不同递归
查找文件名以 test_ 开头的文件
查找以 Test 开头的类(该类不能有 init 方法)
查找以 test_ 开头的函数和方法并进行测试
如果要从默认的查找规则中忽略查找路径,可以加上 --ingore 参数,例如:
pytest--ignore=test_case/xxx.py
3.2 执行选择用例
1、执行单个模块中的全部用例:
py.testtest_demo.py
2、执行指定路径下的全部用例:
py.testsomepath
3、执行字符串表达式中的用例:
py.test-kstringexpr
4、运行指定模块中的某个用例,如运行 test_demo.py 模块中的 test_func 测试函数:
pytesttest_demo.py::test_func
5、运行某个类下的某个用例,如运行 TestClass 类下的 test_method 测试方法:
pytesttest_demo.py::test_method
4.Pytest Fxiture特性
fixture是pytest特有的功能,它用pytest.fixture标识,定义在函数前面。在编写测试函数的时候,可以将此函数名称做为传入参数,pytest 将会以依赖注入方式,将该函数的返回值作为测试函数的传入参数。
pytest.fixture(scope='function',params=None,autouse=False,ids=None)
4.1 作为参数
fixture 可以作为其他测试函数的参数被使用,前提是其必须返回一个值:
@pytest.fixture() defhello(): return"hello" deftest_string(hello): asserthello=="hello","fixtureshouldreturnhello"
4.2 作为 setup
fixture也可以不返回值,这样可以用于在测试方法运行前运行一段代码:
@pytest.fixture()#默认参数,每个测试方法前调用 defbefore(): print('beforeeachtest') deftest_1(before): print('test_1()') @pytest.mark.usefixtures("before") deftest_2(): print('test_2()')
这种方式与setup_method、setup_module等的用法相同,其实它们也是特殊的 fixture。
在上例中,有一个测试用了pytest.mark.usefixtures装饰器来标记使用哪个 fixture,这中用法表示在开始测试前应用该 fixture 函数但不需要其返回值。
4.3 fixture作用范围
fixtrue 可以通过设置 scope 参数来控制其作用域(同时也控制了调用的频率)。如果 scope='module',那么 fixture 就是模块级的,这个 fixture 函数只会在每次相同模块加载的时候执行。这样就可以复用一些需要时间进行创建的对象。fixture 提供四种作用域,用于指定 fixture 初始化的规则:
function:每个测试函数之前执行一次,默认
class: 每个类之前执行一次,
module:每个模块加载之前执行一次
session:每次 session 之前执行一次,即每次测试执行一次
4.4 反向请求
fixture 函数可以通过接受 request 对象来反向获取请求中的测试函数、类或模块上下文。例如:
@pytest.fixture(scope="module") defsmtp(request): importsmtplib server=getattr(request.module,"smtpserver","smtp.qq.com") smtp=smtplib.SMTP(server,587,timeout=5) yieldsmtp smtp.close()
有时需要全面测试多种不同条件下的一个对象,功能是否符合预期。可以通过设置 fixture 的 params 参数,然后通过 request 获取设置的值:
classFoo(object): def__init__(self,a,b,c): self.a=a self.b=b self.c=c defecho(self): print(self.a,self.b,self.c) returnTrue @pytest.fixture(params=[["1","2","3"],["x","y","z"]]) deffoo(request): returnFoo(*request.param) deftest_foo(foo): assertfoo.echo()
设置 params 参数后,运行 test 时将生成不同的测试 id,可以通过 ids 自定义 id:
@pytest.fixture(params=[1,2,4,8],ids=["a","b","c","d"]) defparam_a(request): returnrequest.param deftest_param_a(param_a): printparam_a
4.5 setup/teardown
setup/teardown是指在模块、函数、类开始运行以及结束运行时执行一些动作。比如在一个函数中测试一个数据库应用,测需要在函数开始前连接数据库,在函数运行结束后断开与数据库的连接。setup/teardown是特殊的 fixture,其可以有一下几种实现方式:
#模块级别 defsetup_module(module): pass defteardown_module(module): pass #类级别 @classmethod defsetup_class(cls): pass @classmethod defteardown_class(cls): pass #方法级别 defsetup_method(self,method): pass defteardown_method(self,method): pass #函数级别 defsetup_function(function): pass defteardown_function(function): pass
有时候,还希望有全局的 setup 或 teardown,以便在测试开始时做一些准备工作,或者在测试结束之后做一些清理工作。这可以用 hook 来实现:
defpytest_sessionstart(session): #setup_stuff defpytest_sessionfinish(session,exitstatus): #teardown_stuff
也可以用 fixture 的方式实现:
@fixture(scope='session',autouse=True) defmy_fixture(): #setup_stuff yield #teardown_stuff
4.6 自动执行
有时候需要某些 fixture 在全局自动执行,如某些全局变量的初始化工作,亦或一些全局化的清理或者初始化函数。这时可以通过设置 fixture 的 autouse 参数来让 fixture 自动执行。设置为 autouse=True 即可使得函数默认执行。以下例子会在开始测试前清理可能残留的文件,接着将程序目录设置为该目录:
work_dir="/c/temp" @pytest.fixture(scope="session",autouse=True) defclean_workdir(): shutil.rmtree(work_dir)
5. Pytest Mark特性
Pytest中marker 的作用是,用来标记测试,以便于选择性的执行测试用例。Pytest 提供了一些内建的 marker:
#跳过测试 @pytest.mark.skip(reason=None) #满足某个条件时跳过该测试 @pytest.mark.skipif(condition) #预期该测试是失败的 @pytest.mark.xfail(condition,reason=None,run=True,raises=None,strict=False) #参数化测试函数。给测试用例添加参数,供运行时填充到测试中 #如果parametrize的参数名称与fixture名冲突,则会覆盖掉fixture @pytest.mark.parametrize(argnames,argvalues) #对给定测试执行给定的fixtures #这种用法与直接用fixture效果相同 #只不过不需要把fixture名称作为参数放在方法声明当中 @pytest.mark.usefixtures(fixturename1,fixturename2,...) #让测试尽早地被执行 @pytest.mark.tryfirst #让测试尽量晚执行 @pytest.mark.trylast
其中使用 pytest.skip 和 pytest.xfail 能够实现跳过测试的功能,skip 表示直接跳过测试,而 xfail 则表示存在预期的失败。
除了内建的 markers 外,pytest 还支持没有实现定义的 markers,如:
@pytest.mark.old_test deftest_one(): assertFalse @pytest.mark.new_test deftest_two(): assertFalse @pytest.mark.windows_only deftest_three(): assertFalse
通过使用 -m 参数可以让 pytest 选择性的执行部分测试:
$pytesttest.py-m'notwindows_only'
更详细的关于 marker 的说明可以参考官方文档:https://docs.pytest.org/en/latest/example/markers.html
6. conftest.py文件
从广义理解,conftest.py是一个本地的 per-directory 插件,在该文件中可以定义目录特定的hooks和fixtures。py.test 框架会在它测试的项目中寻找conftest.py文件,然后在这个文件中寻找针对整个目录的测试选项,比如是否检测并运行 doctest 以及应该使用哪种模式检测测试文件和函数。
总结起来,conftest.py 文件大致有如下几种功能:
Fixtures: 用于给测试用例提供静态的测试数据,其可以被所有的测试用于访问,除非指定了范围。
加载插件: 用于导入外部插件或模块:pytest_plugins ="myapp.testsupport.myplugin"
定义钩子: 用于配置钩子(hook),如pytest_runtest_setup、pytest_runtest_teardown、pytest_config等。
测试根路径: 如果将 conftest.py 文件放在项目根路径中,则 pytest 会自己搜索项目根目录下的子模块,并加入到 sys.path 中,这样便可以对项目中的所有模块进行测试,而不用设置 PYTHONPATH 来指定项目模块的位置。
可以有多个conftest.py文件同时存在,其作用范围是目录。例如测试非常复杂时,可以为特定的一组测试创建子目录,并在该目录中创建conftest.py文件,并定义一个 futures 或 hooks。就像如下的结构:
test_case ├──conftest.py ├──module1 │└──conftest.py ├──module2 │└──conftest.py └──module3 └──conftest.py
7. Pytest插件机制
Pytest之所以称之为全功能测试框架,得益于它能通过外部插件或者自定义插件的形式扩展所需的功能,这里推荐几款常用的第三方插件:
pytest-xdist: 分布式测试
pytest-cov: 生成测试覆盖率报告
pytest-pep8: 检测代码是否符合 PEP8 规范
pytest-flakes: 检测代码风格
pytest-html: 生成 html 报告
pytest-randomly: 测试顺序随机
pytest-rerunfailures: 失败重试
pytest-timeout: 超时测试
审核编辑:黄飞
-
自动化测试
+关注
关注
0文章
214浏览量
26958 -
函数
+关注
关注
3文章
4345浏览量
62946 -
python
+关注
关注
56文章
4807浏览量
85016
原文标题:聊一聊,Python自动化测试框架
文章出处:【微信号:TestinChina,微信公众号:Testin云测】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论