耗时2周整理出来了:“ pytest测试框架 ” 保姆级教程(测试小白必看!)
Posted 程序员二黑
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了耗时2周整理出来了:“ pytest测试框架 ” 保姆级教程(测试小白必看!)相关的知识,希望对你有一定的参考价值。
哈喽,我是二黑,这里赠送一份软件测试资料.
- 1、软件测试学习路线
- 2、软件测试视频资料
- 3、软件测试相关文档
- 4、软件测试相关工具、安装包
- 5、高级测试工程师简历模板
- 6、面试题、模拟面试、PDF文档
有需要的小伙伴们可以关注我的公众号:程序员二黑,免费领取
安装
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风格:
-
setUp/tearDown;
-
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。
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 # 执行顺序
测试报告的升级版-allure
pip install pytest-allure-adaptor
测试报告的升级版,功能完备,界面酷炫
资料分享
下面是我收集的一份2021年软件测试资料包
包括:软件测试学习路线 ,软件测试视频资料 ,软件测试相关文档 ,软件测试相关工具、安装包 ,高级测试工程师简历模板 、面试题、模拟面试、PDF文档 ,思维导图等等…希望能帮助到大家。
关注我的微信公众号:程序员二黑,就可以免费领取上面这份资料包啦!
如果你真的想做一件事情,那么就算障碍重重,你也会想尽一切办法去办到它。但若是你不是真心的想要去完成一件事情,那么纵使前方道路平坦,你也会想尽一切理由阻止自己向前。
推荐阅读
高薪程序员也躲不过35岁这一关…当能力与年龄脱节,我们该如何自救
以上是关于耗时2周整理出来了:“ pytest测试框架 ” 保姆级教程(测试小白必看!)的主要内容,如果未能解决你的问题,请参考以下文章