Python Pytest自动化测试框架安装与使用

Time will tell.

Pytest 是 Python 最流行的单元测试框架之一,帮助更便捷的编写测试脚本, 并支持多种功能复杂的测试场景, 能用来做app测试也能用作函数测试。

一、Pytest 优点

  • 允许使用 assert 进行断言
  • 自动识别测试脚本、类、函数
  • 可用于管理小型或者参数类型的测试数据或资源
  • 兼容 unittest 和 nose 测试框架
  • 支持 Python2.7/Python3.4+
  • 丰富的插件支持,超过315个插件支持

二、Pytest 安装

pip install -U pytest

如果提示下面的错误,说明是pip的版本太老了,要更新下:

  Could not find a version that satisfies the requirement pytest (from versions: )
No matching distribution found for pytest

更新方式:

easy_install --upgrade pip

三、官方示例

准备一个test_sample.py内容如下:

def inc(x):
    return x + 1

def test_answer():
    assert inc(3) == 5

在文件所在目录执行:

pytest

做下说明:

  • pytest脚本都以test_xxx.py为文件名;

  • inc方法是我们定义的一个自增函数,该函数将传递进来的参数加1后返回;

  • test_answer是我们编写的一个测试函数,其中我们使用基本的断言语句assert来对结果进行验证,测试函数以test_xxx作为命名。

执行结果如下:

============================================================ test session starts ============================================================
platform darwin -- Python 2.7.15, pytest-4.1.0, py-1.7.0, pluggy-0.8.0
rootdir: /Users/jackey/Documents/iOS/code/iOS-Auto/Agent_Test, inifile:
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:5: AssertionError
========================================================= 1 failed in 0.05 seconds ==========================================================
(wda_python) bash-3.2$ 

当执行到 assert inc(3) == 5 时,报错。

执行pytest会在当前目录和子目录中寻找test_xx.py的测试文件,并进入到测试文件中寻找test_xx开头的测试函数开始执行

执行pytest -q test_xxx.py是执行执行的脚本

在看一个例子,测试指定错误: (Assert that a certain exception is raised)

import pytest

def f():
    raise SystemExit(1)

def test_mytest():
    with pytest.raises(SystemExit):
        f()

执行指令:

pytest -q test_sysexit.py

输出:

(wda_python) bash-3.2$ pytest -q test_sysexit.py 
.                                                                                                                                      [100%]
1 passed in 0.04 seconds
(wda_python) bash-3.2$ 

如果要开发多个测试方法,可以把方法写进一个class中:

class TestClass(object):
    def test_one(self):
        x = 'this'
        assert 'h' in x

    def test_two(self):
        x = 'hello'
        assert hasattr(x, 'check')

Pytest 能够自动识别类中的测试方法, 也不用我们去创建子类或者实实例, 运行结果如下:

(wda_python) bash-3.2$ pytest -q test_sample.py 
.F                                                                                                                                     [100%]
================================================================== FAILURES ==================================================================
_____________________________________________________________ TestClass.test_two _____________________________________________________________

self = <test_sample.TestClass object at 0x102e151d0>

    def test_two(self):
        x = 'hello'
>       assert hasattr(x, 'check')
E       AssertionError: assert False
E        +  where False = hasattr('hello', 'check')

test_sample.py:8: AssertionError
1 failed, 1 passed in 0.08 seconds
(wda_python) bash-3.2$ 

除了直接在脚本路径执行pytest外, 还可以用以下方式:

python -m pytest xxx.py

出现第一个(或第N个)错误时停止:

pytest -x            # stop after first failure
pytest --maxfail=2    # stop after two failures

运行执行测试脚本:

pytest test_mod.py

运行指定目录下的所有脚本:

pytest testing/

运行包含指定关键字的测试方法, 可以是文件名、类名、测试函数名

pytest -k "MyClass and not method"

执行node id运行测试脚本,每一个被收集的测试方法都会分配一个指定的id, 我们可以用一下方式运行执行的测试方法:

# To run a specific test within a module
pytest test_mod.py::test_func     

# To run a test within a class
pytest test_mod.py::TestClass::test_method

日志打印的不同方式:

pytest --showlocals # show local variables in tracebacks
pytest -l           # show local variables (shortcut)

pytest --tb=auto    # (default) 'long' tracebacks for the first and last
                     # entry, but 'short' style for the other entries
pytest --tb=long    # exhaustive, informative traceback formatting
pytest --tb=short   # shorter traceback format
pytest --tb=line    # only one line per failure
pytest --tb=native  # Python standard library formatting
pytest --tb=no      # no traceback at all

四、测试报告

pytest 默认是完整的测试报告, 我们可以加上-r标签显示简短测试报告,可再搭配一下参数。

Here is the full list of available characters that can be used:

f - failed
E - error
s - skipped
x - xfailed
X - xpassed
p - passed
P - passed with output
a - all except pP

可以多个参数一起使用

Debug模式

pytest --pdb

示例:

(wda_python) bash-3.2$ pytest --pdb
========================================================== test session starts ===========================================================
platform darwin -- Python 2.7.15, pytest-4.1.0, py-1.7.0, pluggy-0.8.0
rootdir: /Users/jackey/Documents/iOS/code/iOS-Auto/Agent_Test, inifile:
collected 3 items                                                                                                                        

test_sample.py .F
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> traceback >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

self = <test_sample.TestClass object at 0x10e928610>

    def test_two(self):
        x = 'hello'
>       assert hasattr(x, 'check')
E       AssertionError: assert False
E        +  where False = hasattr('hello', 'check')

test_sample.py:8: AssertionError
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> entering PDB >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
> /Users/jackey/Documents/iOS/code/iOS-Auto/Agent_Test/test_sample.py(8)test_two()
-> assert hasattr(x, 'check')
(Pdb) print x
hello
(Pdb) print hasattr(x,'check')
False
(Pdb) 

还可以指定第几次失败开始进入debug:

pytest -x --pdb   # drop to PDB on first failure, then end test session
pytest --pdb --maxfail=3  # drop to PDB for first three failures

任何失败的异常信息都会存储在sys.last_valuesys.last_type以及 sys_last_traceback

在debug中可以通过以下方式获取最后报错的内容。

(Pdb) import sys
(Pdb) sys.last_traceback.tb_lineno
1357
(Pdb) sys.last_value
AssertionError(u"assert False\n +  where False = hasattr('hello', 'check')",)
(Pdb) 

在执行一开始就进入到debug模式

pytest --trace

输入next执行下一步,exit退出。

脚本中设置断点

import pdb

pdb.set_trace()

例如:

import pdb

class TestClass(object):
    def test_one(self):
        x = 'this'
        pdb.set_trace()
        assert 'h' in x

    def test_two(self):
        x = 'hello'
        assert hasattr(x, 'check')

获取执行最慢的n个测试步骤:

pytest --durations=10

但如果所有脚本的运行时间都小于0.01s, 就不显示了, 除非带上-vv参数

pytest --durations=10 -vv

输出结果:

======================================================= slowest 10 test durations ========================================================
0.00s call     test_sample.py::TestClass::test_two
0.00s setup    test_sysexit.py::test_mytest
0.00s setup    test_sample.py::TestClass::test_two
0.00s setup    test_sample.py::TestClass::test_one
0.00s teardown test_sample.py::TestClass::test_two
0.00s teardown test_sample.py::TestClass::test_one
0.00s call     test_sysexit.py::test_mytest
0.00s teardown test_sysexit.py::test_mytest
0.00s call     test_sample.py::TestClass::test_one
=================================================== 1 failed, 2 passed in 0.06 seconds ===================================================
(wda_python) bash-3.2$ 

将日志保存到指定文件

pytest --resultlog=path

我们也可以在 pytestdemo 脚本中去启动pytest:

import pytest

pytest.main()

执行python pytestdemo.py就可以执行pytest,main()不会抛出SystemExit的异常, 但会返回exitcode,一共有6种exitcode

Exit code 0:    All tests were collected and passed successfully
Exit code 1:    Tests were collected and run but some of the tests failed
Exit code 2:    Test execution was interrupted by the user
Exit code 3:    Internal error happened while executing tests
Exit code 4:    pytest command line usage error
Exit code 5:    No tests were collected

我们试着加上打印

import pytest

print pytest.main()

输出:

(wda_python) bash-3.2$ python pytestDemo.py 
========================================================== test session starts ===========================================================
platform darwin -- Python 2.7.15, pytest-4.1.0, py-1.7.0, pluggy-0.8.0
rootdir: /Users/jackey/Documents/iOS/code/iOS-Auto/Agent_Test, inifile:
collected 3 items                                                                                                                        

test_sample.py .F                                                                                                                  [ 66%]
test_sysexit.py .                                                                                                                  [100%]

================================================================ FAILURES ================================================================
___________________________________________________________ TestClass.test_two ___________________________________________________________

self = <test_sample.TestClass object at 0x1038ba650>

    def test_two(self):
        x = 'hello'
>       assert hasattr(x, 'check')
E       AssertionError: assert False
E        +  where False = hasattr('hello', 'check')

test_sample.py:11: AssertionError
=================================================== 1 failed, 2 passed in 0.05 seconds ===================================================
1
(wda_python) bash-3.2$ 

我们还可以在main中传递参数:

pytest.main(['-q','test_sample.py'])

pytest.main添加plugin,,如下示例在执行的开头和结尾, 添加打印信息。

import pytest

class MyPlugin(object):
    def pytest_sessionfinish(self):
        print '*** Test run reporting finishing'

    def pytest_sessionstart(self):
        print '*** Test run report beginning'

pytest.main(['-q','test_sample.py'], plugins=[MyPlugin()])

输出:

(wda_python) bash-3.2$ python pytestDemo.py 
*** Test run report beginning
.F                                                                                                                                 [100%]*** Test run reporting finishing
================================================================ FAILURES ================================================================
___________________________________________________________ TestClass.test_two ___________________________________________________________
self = <test_sample.TestClass object at 0x1090843d0>
    def test_two(self):
        x = 'hello'
>       assert hasattr(x, 'check')
E       AssertionError: assert False
E        +  where False = hasattr('hello', 'check')
test_sample.py:11: AssertionError
1 failed, 1 passed in 0.05 seconds

好了,以上就到这里了。如果你对更多内容、及Python实例练习题、面试题、自动化软件测试感兴趣可以加入我们175317069一起学习。会有各项学习资源,更有行业深潜多年的技术人分析讲解。

最后祝愿你能成为一名优秀的软件测试工程师!

欢迎【评论】、【点赞】、【关注】~

Time will tell.(时间会证明一切)

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,589评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,615评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,933评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,976评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,999评论 6 393
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,775评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,474评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,359评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,854评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,007评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,146评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,826评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,484评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,029评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,153评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,420评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,107评论 2 356

推荐阅读更多精彩内容