04-13pytest的基本使用方法
有了unittest这个经典的测试框架做铺垫,那么学习其他任何的测试框架都变得有章法可循了。
pytest测试框架也是由unittest改编而来,所以许多地方都是一脉相承。
我相信许多读者再看了unittest的文章之后,已不需要耗费脑细胞就可以把pytest的使用掌握了。你是不是也是其中一个呢?
.\test_assert.py
| |
| |
| |
| def func_number(): |
| return 3 |
| |
| |
| def test_function(): |
| assert func_number() == 3 |
我们把脚本运行一下:
| ...> pytest Stage5\07pytest\pytest test_assert.py -v |
| #### -v 参数能把结果打印的更详细 |
| #### -v, --verbose increase verbosity. |
想要知道更多pytest的参数输入pytest –help
结果如下:
| (testops) D:\Coding\Project\testops\Stage5\07pytest>pytest test_assert.py -v |
| ======================================================== test session starts ======================================================== |
| platform win32 -- Python 3.7.1, pytest-5.2.2, py-1.8.0, pluggy-0.13.0 -- d:\python\virtualenvs\testops\scripts\python.exe |
| cachedir: .pytest_cache |
| rootdir: D:\Coding\Project\testops\Stage5\07pytest |
| collected 1 item |
| |
| test_assert.py::test_function PASSED [100%] |
| |
| ========================================================= 1 passed in 0.01s ========================================================= |
| |
| (testops) D:\Coding\Project\testops\Stage5\07pytest> |
如果你的运行结果看不到详细的信息,那你需要加上 -v 参数。
以上是关于pytest断言的抛砖引玉,就在这章其他小节还有其他具体的使用说明,在下一章节还有实战举例说明。
fixture是测试脚手架的意思。还记得在unittest的文章说起的 厨房脚手架。
上有 抽油烟机顶 ,下游烧火的灶台 ,中间就是厨师的摇摆空间。
在pytest中,fixture的作用得到了改进,不再那么固定和笨重,已经变得 可以移动。
.\test_pytestfixture.py
| |
| import pytest |
| |
| @pytest.fixture() |
| def func_isequal(): |
| a = 'robert' |
| return a |
| |
| def test_equalrobert(func_isequal): |
| assert func_isequal == 'robert' |
| |
| if __name__ == '__main__': |
| pytest.main("-v -s test_pytestfixture.py") |
运行结果如下:
| (testops) D:\Coding\Project\testops\Stage5\07pytest>pytest test_pytestfixture.py -v -s |
| ======================================================== test session starts ======================================================== |
| platform win32 -- Python 3.7.1, pytest-5.2.2, py-1.8.0, pluggy-0.13.0 -- d:\python\virtualenvs\testops\scripts\python.exe |
| cachedir: .pytest_cache |
| rootdir: D:\Coding\Project\testops\Stage5\07pytest |
| collected 1 item |
| |
| test_pytestfixture.py::test_equalrobert PASSED |
| |
| ========================================================= 1 passed in 0.05s ========================================================= |
看文章的你脑袋里面会不会在想,参数是什么?
我想,应该没有。如果有的话,那你还是加我联系方式,单独聊,因为这东西只可意会不可言传。此时旁边的人都发出了笑声。
.\test_parameterized.py
| |
| import pytest |
| import math |
| |
| @pytest.mark.parametrize( |
| "base,exponet,expected", |
| [(2,3,8), |
| (2,4,16), |
| (2,5,32), |
| (2,6,64)], |
| ids =["case1_**3","case2_**4","case3_**5","case4_**6"] |
| ) |
| def test_pow(base,exponet,expected): |
| assert math.pow(base,exponet) ==expected |
上面的例子传了3个参数 argnames, argvalues, ids
分别代表,参数名称,参数值,用例名称
这个测试用例是对幂运算的结果进行验证,例如 (2,3,8) 是 指 底数是2,指数是3,预期结果是8 .
其中用到了python 内置math模块的pow方法。
| (testops) D:\Coding\Project\testops\Stage5\07pytest>pytest test_parameterized.py -v |
| ======================================================== test session starts ======================================================== |
| platform win32 -- Python 3.7.1, pytest-5.2.2, py-1.8.0, pluggy-0.13.0 -- d:\python\virtualenvs\testops\scripts\python.exe |
| cachedir: .pytest_cache |
| rootdir: D:\Coding\Project\testops\Stage5\07pytest |
| collected 4 items |
| |
| test_parameterized.py::test_pow[case1_**3] PASSED [ 25%] |
| test_parameterized.py::test_pow[case2_**4] PASSED [ 50%] |
| test_parameterized.py::test_pow[case3_**5] PASSED [ 75%] |
| test_parameterized.py::test_pow[case4_**6] PASSED [100%] |
| |
| ========================================================= 4 passed in 0.02s ========================================================= |
前面我们运行的时候,都是在terminal下,输入命令 pytest test_*.py来运行。
实际上pytest提供了不少其他运行指令,我们可以通过pytest –help查看。
-k 运行名称中包含某字符的测试用例
-q 减少测试的运行冗长
-x 如果出现一天测试用例失败,则退出测试
-v 输出更详细的日志信息
下面是一个测试py文件
.\test_runrule.py
| |
| |
| |
| |
| def func(x,y): |
| return x+y >10 |
| |
| def func_math(x,y): |
| import math |
| return math.fabs(x) |
| |
| def test_isnottrue(): |
| assert func(3,3) is True |
| |
| def test_func_math(): |
| assert func_math(3.333,6.222) >0 |
我们运行上面的test_runrule.py
1.运行命令: pytest test_runrule.py
| (testops) D:\Coding\Project\testops\Stage5\07pytest>pytest test_runrule.py |
| ======================================================== test session starts ======================================================== |
| platform win32 -- Python 3.7.1, pytest-5.2.2, py-1.8.0, pluggy-0.13.0 |
| rootdir: D:\Coding\Project\testops\Stage5\07pytest |
| collected 2 items |
| |
| test_runrule.py F. [100%] |
| |
| ============================================================= FAILURES ============================================================== |
| __________________________________________________________ test_isnottrue ___________________________________________________________ |
| |
| def test_isnottrue(): |
| > assert func(3,3) is True |
| E assert False is True |
| E + where False = func(3, 3) |
| |
| test_runrule.py:13: AssertionError |
| ==================================================== 1 failed, 1 passed in 0.03s ==================================================== |
运行命令: pytest test_runrule.py –junit-xml=./report/log.xml
| (testops) D:\Coding\Project\testops\Stage5\07pytest>pytest test_runrule.py --junit-xml=./report/log.xml |
| ======================================================== test session starts ======================================================== |
| platform win32 -- Python 3.7.1, pytest-5.2.2, py-1.8.0, pluggy-0.13.0 |
| rootdir: D:\Coding\Project\testops\Stage5\07pytest |
| collected 2 items |
| |
| test_runrule.py F. [100%] |
| |
| ============================================================= FAILURES ============================================================== |
| __________________________________________________________ test_isnottrue ___________________________________________________________ |
| |
| def test_isnottrue(): |
| > assert func(3,3) is True |
| E assert False is True |
| E + where False = func(3, 3) |
| |
| test_runrule.py:14: AssertionError |
| --------------------------- generated xml file: D:\Coding\Project\testops\Stage5\07pytest\report\log.xml ---------------------------- |
| ==================================================== 1 failed, 1 passed in 0.03s ==================================================== |
查看report\log.xml如下
| <?xml version="1.0" encoding="utf-8"?><testsuites><testsuite errors="0" failures="1" hostname="DESKTOP-V5FAHI8" name="pytest" skipped="0" tests="2" time="0.030" timestamp="2019-11-01T17:10:51.928076"><testcase classname="test_runrule" file="test_runrule.py" line="12" name="test_isnottrue" time="0.000"><failure message="assert False is True |
| + where False = func(3, 3)">def test_isnottrue(): |
| > assert func(3,3) is True |
| E assert False is True |
| E + where False = func(3, 3) |
| |
| test_runrule.py:14: AssertionError</failure></testcase><testcase classname="test_runrule" file="test_runrule.py" line="15" name="test_func_math" time="0.000"></testcase></testsuite></testsuites> |
看了这么长的代码或者xml,是不是觉得这报告样式太难看了,无法入眼。
其实,还有许多其他排版很漂亮的报告。例如这个是pytest的html报告模板。
后面会有针对报告的小节来进行详细记述。
./conftest.py
| import pytest |
| |
| |
| @pytest.fixture() |
| def test_url(): |
| return "http://www.qq.com" |
| return "http://www.baidu.com" |
./test_qq.py
| def test_qq(test_url): |
| print(test_url) |
confset.py是pytest特有的本地测试配置文件,即可以用来设置项目级别的Fixture,也可以用来导入外部插件,还可以用来指定钩子函数。
1 断言 的关键字assert
2 脚手架的定义需要用到 @pytest.fixture() 内置方法
3 参数化用到的关键字:@pytest.mark.parametrize()
4 运行测的常用指令
-v
-s
-q
5 生成测试报告的方法有多
6 conftest.py名字是固定的,不可以被修改
1 pytest中fixture源码解析
| def parametrize(self,argnames, argvalues, indirect=False, ids=None, scope=None): |
| """ Add new invocations to the underlying test function using the list |
| of argvalues for the given argnames. Parametrization is performed |
| during the collection phase. If you need to setup expensive resources |
| see about setting indirect to do it rather at test setup time. |
| |
| :arg argnames: a comma-separated string denoting one or more argument |
| names, or a list/tuple of argument strings. |
| |
| :arg argvalues: The list of argvalues determines how often a |
| test is invoked with different argument values. If only one |
| argname was specified argvalues is a list of values. If N |
| argnames were specified, argvalues must be a list of N-tuples, |
| where each tuple-element specifies a value for its respective |
| argname. |
| |
| :arg indirect: The list of argnames or boolean. A list of arguments' |
| names (self,subset of argnames). If True the list contains all names from |
| the argnames. Each argvalue corresponding to an argname in this list will |
| be passed as request.param to its respective argname fixture |
| function so that it can perform more expensive setups during the |
| setup phase of a test rather than at collection time. |
| |
| :arg ids: list of string ids, or a callable. |
| If strings, each is corresponding to the argvalues so that they are |
| part of the test id. If None is given as id of specific test, the |
| automatically generated id for that argument will be used. |
| If callable, it should take one argument (self,a single argvalue) and return |
| a string or return None. If None, the automatically generated id for that |
| argument will be used. |
| If no ids are provided they will be generated automatically from |
| the argvalues. |
| |
| :arg scope: if specified it denotes the scope of the parameters. |
| The scope is used for grouping tests by parameter instances. |
| It will also override any fixture-function defined scope, allowing |
| to set a dynamic scope using test context or configuration. |
| """ |
:arg ids: list of string ids, or a callable. 这里可以定义测试用例的名称,如果有N个测试的时候,这是个很棒的用法。