内容简介:一个简单的例子:执行结果
安装
pip install pytest
简介
pytest
可以轻松编写测试,支持扩展,并且有丰富的引用和库支持复杂的功能测试
一个简单的例子:
# content of test_sample.py def inc(x): return x + 1 def test_answer(): assert inc(3) == 5
执行结果
$ pytest =========================== test session starts ============================ platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 1 item test_sample.py F [100%] ================================= FAILURES ================================= _______________________________ test_answer ________________________________ def test_answer(): > assert inc(3) == 5 E assert 4 == 5 E + where 4 = inc(3) test_sample.py:6: AssertionError ========================= 1 failed in 0.12 seconds =========================
功能特点
- 失败的语句有详尽的信息 (无需记住 self.assert* names);
- 自动发现测试的模块和方法(通过模块和功能的命名方式);
- 使用fixtures用于管理测试资源;
- 可以兼容 unittest和nose测试组件;
- Python 2.7, Python 3.4+, PyPy 2.3, Jython 2.5 (未经测试);
- 丰富的插件资源, 超过315个外部插件和蓬勃发展的社区;
- 支持参数化
- 执行测试过程中可以将某些测试跳过,或者对某些预期失败的case标记成失败
- 支持重复执行失败的case
fixtures
fixtures提供一个固定的基线,可以可靠地重复执行测试。pytest fixture比经典的xUnit的setup/teardown 功能提供了显着的改进:
- fixtures具有明确的名称,并通过从测试功能,模块,类或整个项目中声明它们的使用来激活。
- fixtures以模块化方式实现,因为每个fixtures名称触发fixtures方法,该fixtures方法本身可以使用其他fixtures。
- fixtures管理从简单的单元扩展到复杂的功能测试,允许根据配置和组件选项对fixtures和测试进行参数化,或者在功能,类,模块或整个测试会话范围内重复使用fixtures。
此外,pytes也支持经典的 xunit风格 。您可以根据需要混合使用两种样式,逐步从经典样式移动到新样式。您也可以从现有的unittest.TestCase样式或基于nose的项目开始。
xunit风格:
setUpClass/tearDownClass setUpModule/tearDownModule
拓展作用域:
- 模块级(setup_module/teardown_module)开始于模块始末,全局的
- 函数级(setup_function/teardown_function)只对函数用例生效(不在类中)
- 类级(setup_class/teardown_class)只在类中前后运行一次(在类中)
- 方法级(setup_method/teardown_method)开始于方法始末(在类中)
- 类里面的(setup/teardown)运行在调用方法的前后
fixtures作为函数参数
让我们看一个简单的独立测试模块,它包含一个fixture和一个使用它的测试函数
# content of ./test_smtpsimple.py import pytest @pytest.fixture def smtp_connection(): import smtplib return smtplib.SMTP("smtp.gmail.com", 587, timeout=5) def test_ehlo(smtp_connection): response, msg = smtp_connection.ehlo() assert response == 250 assert 0 # for demo purposes
在这里, test_ehlo
需要 smtp_connectio的返回值。pytest发现并调用
@pytest.fixture 标记的
smtp_connection fixture函数。运行测试如下所示:
$ pytest test_smtpsimple.py =========================== test session starts ============================ platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 1 item test_smtpsimple.py F [100%] ================================= FAILURES ================================= ________________________________ test_ehlo _________________________________ smtp_connection = <smtplib.SMTP object at 0xdeadbeef> def test_ehlo(smtp_connection): response, msg = smtp_connection.ehlo() assert response == 250 > assert 0 # for demo purposes E assert 0 test_smtpsimple.py:11: AssertionError ========================= 1 failed in 0.12 seconds =========================
pytest 调用执行过程如下:
- pytest 发现了test_ehlo函数,因为以test_前缀。test_ehlo需要一个名为smtp_connection的函数参数。通过查找名为标记fixture的函数来发现匹配smtp_connection函数
- smtp_connection() 被调用来创建一个实例
- test_ehlo(<smtp_connection instance>) 被调用
共享fixture功能
如果在实施测试期间您意识到要使用多个测试文件中的fixture功能,则可以将其移动到conftest.py文件中。您不需要导入要在测试中使用的夹具,它会自动被pytest发现。
下面这个示例将fixture函数放入单独的 conftest.py
文件中,以便来自目录中多个测试模块的测试可以访问fixture函数
# content of conftest.py import pytest import smtplib @pytest.fixture(scope="module") def smtp_connection(): return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
这个fixture的名字是smtp_connection,可以在任何测试文件(conftest.py所在目录中下的)将名称列为输入参数来访问:
# content of test_module.py def test_ehlo(smtp_connection): response, msg = smtp_connection.ehlo() assert response == 250 assert b"smtp.gmail.com" in msg assert 0 # for demo purposes def test_noop(smtp_connection): response, msg = smtp_connection.noop() assert response == 250 assert 0 # for demo purposes
fixture作用域
@pytest.fixture(scope="module")使用scope控制fixture的作用域,function,class,module,package,session。
变量名称 | 作用范围 | xunit风格对比 |
---|---|---|
function | 开始于方法始末(在类中)默认 | setup_method |
class | 只在类中前后运行一次,每一个类调用一次,一个类可以有多个方法 | setup_class |
module | 开始于模块始末,全局的,每一个.py文件调用一次,该文件内又有多个function和class | setup_module |
|
|
|
session | 是多个文件调用一次,可以跨.py文件调用,每个.py文件就是module |
fixture的teardown 代码
用xunit风格的的代码setup和teardown都是成对出现的,pytest除了兼容这种模式外,pytest还支持fixture特定的终结代码的执行。通过使用 yield
语句而不是 return
, yield
语句之后的所有代码都用作teardown代码:
# content of conftest.py import smtplib import pytest @pytest.fixture(scope="module") def smtp_connection(): smtp_connection = smtplib.SMTP("smtp.gmail.com", 587, timeout=5) yield smtp_connection # provide the fixture value print("teardown smtp") smtp_connection.close()
当模块中的最后一次测试已经完成,无论测试的情况如何的语句smtp_connection.close()将被执行
让我们执行:
$ pytest -s -q --tb=no FFteardown smtp 2 failed in 0.12 seconds
请注意,如果我们使用 scope='function'
夹具设置fixture修饰的方法,则每次单独测试都会进行清理
我们也可以使用 yield
语法 with
的语句
# content of test_yield2.py import smtplib import pytest @pytest.fixture(scope="module") def smtp_connection(): with smtplib.SMTP("smtp.gmail.com", 587, timeout=5) as smtp_connection: yield smtp_connection # provide the fixture value
yield关键字是在 python 语法生成器使用,用来节省内存
参数化
fixture参数化
当fixture方法被多次调用,并且每次执行一组相同的测试,在这种情况下,可以对fixture方法进行参数化。
扩展前面的示例,我们可以通过标记fixture的方法创建两个 smtp_connection 实例。fixture函数通过request对象访问每个参数:
# content of conftest.py import pytest import smtplib @pytest.fixture(scope="module", params=["smtp.gmail.com", "mail.python.org"]) def smtp_connection(request): smtp_connection = smtplib.SMTP(request.param, 587, timeout=5) yield smtp_connection print("finalizing %s" % smtp_connection) smtp_connection.close()
运行测试:
$ pytest -q test_module.py FFFF [100%] ================================= FAILURES ================================= ________________________ test_ehlo[smtp.gmail.com] _________________________ smtp_connection = <smtplib.SMTP object at 0xdeadbeef> def test_ehlo(smtp_connection): response, msg = smtp_connection.ehlo() assert response == 250 assert b"smtp.gmail.com" in msg > assert 0 # for demo purposes E assert 0 test_module.py:6: AssertionError ________________________ test_noop[smtp.gmail.com] _________________________ smtp_connection = <smtplib.SMTP object at 0xdeadbeef> def test_noop(smtp_connection): response, msg = smtp_connection.noop() assert response == 250 > assert 0 # for demo purposes E assert 0 test_module.py:11: AssertionError ________________________ test_ehlo[mail.python.org] ________________________ smtp_connection = <smtplib.SMTP object at 0xdeadbeef> def test_ehlo(smtp_connection): response, msg = smtp_connection.ehlo() assert response == 250 > assert b"smtp.gmail.com" in msg E AssertionError: assert b'smtp.gmail.com' in b'mail.python.org\nPIPELINING\nSIZE 51200000\nETRN\nSTARTTLS\nAUTH DIGEST-MD5 NTLM CRAM-MD5\nENHANCEDSTATUSCODES\n8BITMIME\nDSN\nSMTPUTF8\nCHUNKING' test_module.py:5: AssertionError -------------------------- Captured stdout setup --------------------------- finalizing <smtplib.SMTP object at 0xdeadbeef> ________________________ test_noop[mail.python.org] ________________________ smtp_connection = <smtplib.SMTP object at 0xdeadbeef> def test_noop(smtp_connection): response, msg = smtp_connection.noop() assert response == 250 > assert 0 # for demo purposes E assert 0 test_module.py:11: AssertionError ------------------------- Captured stdout teardown ------------------------- finalizing <smtplib.SMTP object at 0xdeadbeef> 4 failed in 0.12 seconds
我们看到两个测试函数分别针对不同的smtp_connection实例运行了两次
测试函数参数
内置的pytest.mark.parametrize装饰器支持测试函数的参数的参数化。以下是测试函数的典型示例,该函数实现检查某个输入是否导致预期输出:
# content of test_expectation.py import pytest @pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)]) def test_eval(test_input, expected): assert eval(test_input) == expected
次使用它们运行三次:
$ pytest =========================== test session starts ============================ platform linux -- Python 3.x.y, pytest-4.x.y, py-1.x.y, pluggy-0.x.y cachedir: $PYTHON_PREFIX/.pytest_cache rootdir: $REGENDOC_TMPDIR collected 3 items test_expectation.py ..F [100%] ================================= FAILURES ================================= ____________________________ test_eval[6*9-42] _____________________________ test_input = '6*9', expected = 42 @pytest.mark.parametrize("test_input,expected", [("3+5", 8), ("2+4", 6), ("6*9", 42)]) def test_eval(test_input, expected): > assert eval(test_input) == expected E AssertionError: assert 54 == 42 E + where 54 = eval('6*9') test_expectation.py:6: AssertionError ==================== 1 failed, 2 passed in 0.12 seconds ====================
调用fixture的方式
# content of conftest.py import pytest import smtplib @pytest.fixture(scope="module") def smtp_connection(): return smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
在方法中可以直接使用fixture标识的函数名调用:
# content of test_module.py def test_ehlo(smtp_connection): response, msg = smtp_connection.ehlo() assert response == 250 assert b"smtp.gmail.com" in msg assert 0 # for demo purposes
也可以用声明装饰器@pytest.mark.usefixtures调用
# content of test_module.py @pytest.mark.usefixtures("smtp_connection") def test_ehlo(): response, msg = smtp_connection.ehlo() assert response == 250 assert b"smtp.gmail.com" in msg assert 0 # for demo purposes
当装饰器@pytest.mark.usefixtures作用于类的时,如果这个 @pytest.fixture的scope=function,那么类中的每个测试方法都会调用这个fixture。
@pytest.fixture(scope="module", autouse=True),参数autouse, 默认设置为False。 当默认为False,就可以选择用上面两种方式来试用fixture。 当设置为True时,在一个session内的所有的test都会自动调用这个fixture。 所以用该功能时也要谨慎小心
pytest常用插件
pip install pytest-html #轻量级的测试报告 pytest '文件' --html=report.html pip install pytest-sugar # 打印进度 pip install pytest-rerunfailures # 失败重试 pip install pytest-ordering # 执行顺序 pip install pytest-allure-adaptor #测试报告的升级版,功能完备,界面酷炫
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Book of CSS3
Peter Gasston / No Starch Press / 2011-5-13 / USD 34.95
CSS3 is the technology behind most of the eye-catching visuals on the Web today, but the official documentation can be dry and hard to follow. Luckily, The Book of CSS3 distills the heady technical la......一起来看看 《The Book of CSS3》 这本书的介绍吧!