pytest测试框架
单元测试最初流行是从JUnit,而他的发明者Kent Beck大神的经典名作<测试驱动开发>这本小书却引领了TDD的风潮,产生了xUnit各个语言系列.
初次接触pytest,感觉不像个xUnit,因为既看不到对TestCase的继承,也找不到Setup/Teardown这些建立/销毁函数.当然这些基本功能肯定是支持的.
pytest虽然作为一个Python单元测试框架的扩展版, 但是它丰富的功能和灵活的特性也很适合做功能测试, 其中的精华就是fixtures.
基础
- 测试类以Test开头
- 测试方法以test开头
py.test 用法
pytest提供丰富的命令行参数.
pytest -h
# 其他用法
-q, --quiet
-s --capture=no : 默认通过时不会打印print语句
--cache-show
--cache-clear
下面以测试过程为序,展开各过程主要用法.
配置
pytest配置文件: pytest.ini|tox.ini|setup.cfg
收集
pytest.mark
@pytest.mark.webtest
注: 只作用于tests,对fixture无效
命令行测试用例选择:
- -k EXPRESSION : 按正则选取
- -m MARKEXPR : 示例 “not (slow or long)”
- --ignore=path 忽略部分测试集
- --collectonly 只是收集用例
# 只允许标记webtest的用例
pytest -v -m webtest
# 运行所有除了webtest的用例
pytest -v -m "not webtest"
运行
pytest也支持运行unittest格式的用例.
基本用法:
pytest test_mod.py::TestClass::test_method
命令行辅助选项:
--lf, --last-failed rerun only the tests that failed at the last run (or all if none failed)
# 退出条件:
-x, --exitfirst 出错后退出
--maxfail=num 失败N次后退出
包含上级模块到包路径
集中方式
# 在根目录创建空文件conftest.py
# 或 python -m pytest tests/
# 或设置环境变量 PYTHONPATH
# 或直接修改代码,添加
import sys, os
testPath = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, testPath + '/../')
报告
# 需要先安装pytest-html插件
py.test --html=report.html
# 命令行选项:
--durations=N # 显示最慢的执行 (N=0 for all)
Setup/Teardown
尽管pytest支持JUnit类型的Setup/Teardown,但通常推荐使用更灵活的fixture方式.
经典方式的用法:
def setup_module(module):
print("setting up MODULE {0}".format(module.__name__))
class TestBasic:
def setup_class(cls):
print("setting up CLASS {0}".format(cls.__name__))
def setup_method(self, method):
print("setting up METHOD {0}".format(method.__name__))
def test_demo(self):
assert False
fixture
fixtures通常存放在根目录的conftest.py或子文件夹的conftest.py(可用来覆盖并重新定义).
装饰器标记函数为fixture, 总体上fixtures提供:
- 单元测试框架的基本功能
- 依赖注入: 一些共有函数值/帮助函数/Mock等
- 测试数据参数化
- 灵活性由不同的作用范围和目录级conftest.py等提供
例子
import pytest
import tempfile
import shutil
import os.path
@pytest.fixture
def temp_dir(request):
dir = tempfile.mkdtemp()
print(dir)
# 使用 yield
yield dir
# 之后的语句对应teardown
shutil.rmtree(dir)
def test_osfiles(temp_dir):
os.mkdir(os.path.join(temp_dir, "a"))
os.mkdir(os.path.join(temp_dir, "b"))
dir_contents = os.listdir(temp_dir)
assert len(dir_contents) == 2
assert "a" in dir_contents
assert "b" in dir_contents
fixture的用法
@pytest.fixture()
主要参数:
- scope: 生命周期可以为 session/module/class
- params: 参数化,会执行多次
- autouse=True
tear down函数体定义:
- 代码块为yield之后的所有语句
- 或者使用request.addfinalizer(fin)的方式 (好处:及时发生异常也会执行)
标记或忽略用例
pytest.skip结合if语句 等价于 @pytest.mark.skipif(“sys.version_info <= (3,0)”)
参数化
- request.params
- @pytest.mark.parametrize
- pytest_generate_tests
注:可以用ids参数更新测试名称/override function name
tests之间共享数据
@pytest.fixture(scope="module")
def data():
return {"key1": None, "key2": "value2"}
test run之间共享数据: cache
# 通过request fixture
request.config.cache.set('shared','a')
assert request.config.cache.get('shared',None) == 'a'
命令行辅助项
- --fixtures 显示可用的fixture
- --setup-only only setup fixtures, do not execute tests.
- --setup-show show setup of fixtures while executing tests.
- --setup-plan show what fixtures and tests would be executed but don’t execute anything.
内置的fixture
- tmpdir
- pytestconfig
- cache
- monkeypatch
- capsys
- doctest_namespace
- recwarn
# monkeypatch: mock模块和环境
monkeypatch.setattr()
monkeypatch.delattr()
功能介绍
日志相关
# 命令行参数
--log-level
--log-cli-level
报告
# 生成HTML报告,需插件支持
pytest --html=report.html
# 生成JUnit兼容报告,方便与Jenkins集成
pytest --junitxml=junit-report.xml
扩展资源
流行插件
- pytest-variables : 通过json文件定义共享变量
- pytest-timeout: 测试超时插件,避免长时间异常运行
- pytest-ordering: 指定测试顺序
- pytest-dependency: 测试依赖关系
- pytest-xdist : 并行/分布式执行测试
- pytest-html : HTML测试报告
示例
# 指定执行顺序
# 原理是修改hook: pytest_collection_modifyitems
@pytest.mark,first
@pytest.mark.order#1
@pytest.mark.run(order=1)
# 超时设置
@pytest.mark.timeout(60)
# 依赖
@pytest.mark.dependency(name="b")
@pytest.mark.dependency(name="e", depends=["b", "c"])