• iOS自动化探索(四)自动化测试框架pytest


    自动化测试框架 - pytest

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

    官方文档: https://docs.pytest.org/en/latest/

    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_value,sys.last_type 以及 sys_last_traceback

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

    (Pdb) import sys
    (Pdb) sys.last_traceback.tb_lineno
    1357
    (Pdb) sys.last_value
    AssertionError(u"assert False
     +  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
    ======================================================= slowest 10 test durations ========================================================
    
    (0.00 durations hidden.  Use -vv to show these durations.)

    但如果所有脚本的运行时间都小于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

    Disabling plugins

    To disable loading specific plugins at invocation time, use the -p option together with the prefix no:.

    Example: to disable loading the plugin doctest, which is responsible for executing doctest tests from text files, invoke pytest like this:

    pytest -p no:doctest

    我们也可以在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

  • 相关阅读:
    一道简单正则面试题
    给公司搭建Nuget服务
    [转]const使用详解 D
    MFC学习记录提取16进制表示码(位操作) D
    SQL Server中更新视图, 可能出现的错误及处理
    linux设置私钥登陆
    Notebook里怎样使用argparse
    本地MAC上传文件到服务器
    Springboot @Value注解 注入Integer类型
    C# 自定义委托与事件应用
  • 原文地址:https://www.cnblogs.com/zhouxihi/p/10244320.html
Copyright © 2020-2023  润新知