pytest
也支持执行指定用例:
- 指定测试文件路径
pytest /path/to/test/file.py
- 指定测试类
pytest /path/to/test/file.py:TestCase
- 指定测试方法
pytest another.test::TestClass::test_method
- 指定测试函数
pytest /path/to/test/file.py:test_function
四、测试夹具(Fixtures)
pytest
的测试夹具和 unittest
、nose
、nose2
的风格迥异,它不但能实现 setUp
和 tearDown
这种测试前置和清理逻辑,还其他非常多强大的功能。
4.1 声明和使用
pytest
中的测试夹具更像是测试资源,你只需定义一个夹具,然后就可以在用例中直接使用它。得益于 pytest
的依赖注入机制,你无需通过from xx import xx
的形式显示导入,只需要在测试函数的参数中指定同名参数即可,比如:
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
上述示例中定义了一个测试夹具 smtp_connection
,在测试函数 test_ehlo
签名中定义了同名参数,则 pytest
框架会自动注入该变量。
4.2 共享
在 pytest
中,同一个测试夹具可被多个测试文件中的多个测试用例共享。只需在包(Package)中定义 conftest.py
文件,并把测试夹具的定义写在该文件中,则该包内所有模块(Module)的所有测试用例均可使用 conftest.py
中所定义的测试夹具。
比如,如果在如下文件结构的 test_1/conftest.py
定义了测试夹具,那么 test_a.py
和 test_b.py
可以使用该测试夹具;而 test_c.py
则无法使用。
-- test_1 | |-- conftest.py |
– test_a.py
| -- test_b.py
– test_2
`-- test_c.py
4.3 生效级别
unittest
和 nose
均支持测试前置和清理的生效级别:测试方法、测试类和测试模块。
pytest
的测试夹具同样支持各类生效级别,且更加丰富。通过在 pytest.fixture 中指定 scope
参数来设置:
- function —— 函数级,即调用每个测试函数前,均会重新生成 fixture
- class —— 类级,调用每个测试类前,均会重新生成 fixture
- module —— 模块级,载入每个测试模块前,均会重新生成 fixture
- package —— 包级,载入每个包前,均会重新生成 fixture
- session —— 会话级,运行所有用例前,只生成一次 fixture
当我们指定生效级别为模块级时,示例如下:
import pytest
import smtplib
@pytest.fixture(scope=“module”)
def smtp_connection():
return smtplib.SMTP(“smtp.gmail.com”, 587, timeout=5)
4.4 测试前置和清理
pytest
的测试夹具也能够实现测试前置和清理,通过 yield
语句来拆分这两个逻辑,写法变得很简单,如:
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()
在上述示例中,yield smtp_connection
及前面的语句相当于测试前置,通过 yield
返回准备好的测试资源 smtp_connection
; 而后面的语句则会在用例执行结束(确切的说是测试夹具的生效级别的声明周期结束时)后执行,相当于测试清理。
如果生成测试资源(如示例中的 smtp_connection
)的过程支持 with
语句,那么还可以写成更加简单的形式:
@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
pytest
的测试夹具除了文中介绍到的这些功能,还有诸如参数化夹具、工厂夹具、在夹具中使用夹具等更多高阶玩法,详情请阅读 “pytest fixtures: explicit, modular, scalable”。
五、跳过测试和预计失败
pytest
除了支持 unittest
和 nosetest
的跳过测试和预计失败的方式外,还在 pytest.mark
中提供对应方法:
- 通过 skip 装饰器或 pytest.skip 函数直接跳过测试
- 通过 skipif按条件跳过测试
- 通过 xfail 预计测试失败
示例如下:
@pytest.mark.skip(reason=“no way of currently testing this”)
def test_mark_skip():
…
def test_skip():
if not valid_config():
pytest.skip(“unsupported configuration”)
@pytest.mark.skipif(sys.version_info < (3, 6), reason=“requires python3.6 or higher”)
def test_mark_skip_if():
…
@pytest.mark.xfail
def test_mark_xfail():
…
关于跳过测试和预计失败的更多玩法,参见 “Skip and xfail: dealing with tests that cannot succeed”
六、子测试/参数化测试
pytest
除了支持 unittest
中的 TestCase.subTest
,还支持一种更为灵活的子测试编写方式,也就是 参数化测试
,通过 pytest.mark.parametrize
装饰器实现。
在下面的示例中,定义一个 test_eval
测试函数,通过 pytest.mark.parametrize
装饰器指定 3 组参数,则将生成 3 个子测试:
@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
示例中故意让最后一组参数导致失败,运行用例可以看到丰富的测试结果输出:
========================================= test session starts =========================================
platform darwin – Python 3.7.1, pytest-4.0.1, py-1.7.0, pluggy-0.8.0
rootdir: /Users/prodesire/projects/tests, inifile:
plugins: cov-2.6.0
collected 3 items
test.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.py:6: AssertionError
================================= 1 failed, 2 passed in 0.09 seconds ==================================
若将参数换成 pytest.param
,我们还可以有更高阶的玩法,比如知道最后一组参数是失败的,所以将它标记为 xfail:
@pytest.mark.parametrize(
“test_input,expected”,
[(“3+5”, 8), (“2+4”, 6), pytest.param(“6*9”, 42, marks=pytest.mark.xfail)],
)
def test_eval(test_input, expected):
assert eval(test_input) == expected
如果测试函数的多个参数的值希望互相排列组合,我们可以这么写:
@pytest.mark.parametrize(“x”, [0, 1])
@pytest.mark.parametrize(“y”, [2, 3])
def test_foo(x, y):
pass
上述示例中会分别把 x=0/y=2
、x=1/y=2
、x=0/y=3
和x=1/y=3
带入测试函数,视作四个测试用例来执行。
七、测试结果输出
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数软件测试工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年软件测试全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上软件测试开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注软件测试)
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**
如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注软件测试)
[外链图片转存中…(img-zKspRNs0-1713019898230)]
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!