ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

pytest自动化测试

2020-05-16 16:56:10  阅读:278  来源: 互联网

标签:username setup py pytest 测试 自动化 test def


摘要

pytest是成熟的功能齐全的Python测试工具,有助于编写更好的程序。

pytest基本知识

参考官方文档翻译过来,了解一下pytest知识点

        pytest中可以按节点ID运行测试。

在命令行中指定测试方法的另一个示例:

pytest test_mod.py::TestClass::test_method

        通过标记表达式运行测试

pytest -m slow

将运行用@pytest.mark.slow装饰器装饰的所有测试。

       详细的总结报告

pytest –ra

       分析测试执行持续时间

要获取最慢的10个测试持续时间的列表,请执行以下操作:

pytest --durations=10

       将测试报告发送到在线pastebin服务

为每个测试失败创建一个URL:

pytest --pastebin=failed

为整个测试会话日志创建一个URL

pytest --pastebin=all

        从python代码中调用pytest

pytest.main()

注意:

调用pytest.main()将导致导入你的测试及其导入的任何模块。由于python导入系统的缓存机制,pytest.main()从同一进程进行后续调用不会反映两次调用之间对这些文件的更改。因此,pytest.main()不建议从同一进程进行多次调用(例如,以重新运行测试)。

         断言

使用标准python assert来验证python测试中的期望和值。

        conftest.py

对于可能的值scope有:function,class,module,package或session。

  1. pytest将建立一个字符串,它是用于在参数化fixture,例如每个fixtures测试ID。这些ID可以用于-k选择要运行的特定情况,并且当一个故障发生时,它们还将标识特定情况。使用pytest运行--collect-only将显示生成的ID。
  2.  使用直接测试参数化fixture

给定测试文件的结构为:

tests/
    __init__.py
    conftest.py
        # content of tests/conftest.py
        import pytest
        @pytest.fixture
        def username():
            return 'username'
        @pytest.fixture
        def other_username(username):
            return 'other-' + username
 
    test_something.py
        # content of tests/test_something.py
        import pytest
 
        @pytest.mark.parametrize('username', ['directly-overridden-username'])
        def test_username(username):
            assert username == 'directly-overridden-username'
 
        @pytest.mark.parametrize('username', ['directly-overridden-username-other'])
        def test_username_other(other_username):
            assert other_username == 'other-directly-overridden-username-other'

使用非参数化的参数覆盖参数化fixture

给定测试文件的结构为:

tests/
    __init__.py
 
    conftest.py
        # content of tests/conftest.py
        import pytest
 
        @pytest.fixture(params=['one', 'two', 'three'])
        def parametrized_username(request):
            return request.param
 
        @pytest.fixture
        def non_parametrized_username(request):
            return 'username'
 
    test_something.py
        # content of tests/test_something.py
        import pytest
 
        @pytest.fixture
        def parametrized_username():
            return 'overridden-username'
 
        @pytest.fixture(params=['one', 'two', 'three'])
        def non_parametrized_username(request):
            return request.param
 
        def test_username(parametrized_username):
            assert parametrized_username == 'overridden-username'
 
        def test_parametrized_username(non_parametrized_username):
            assert non_parametrized_username in ['one', 'two', 'three']
 
    test_something_else.py
        # content of tests/test_something_else.py
        def test_username(parametrized_username):
            assert parametrized_username in ['one', 'two', 'three']
 
        def test_username(non_parametrized_username):
            assert non_parametrized_username == 'username'

monkeypatch

简单的api获取例子

# contents of app.py, a simple API retrieval example

import requests

 

 

def get_json(url):

    """Takes a URL, and returns the JSON."""

    r = requests.get(url)

    return r.json()

此外,如果该模拟程序旨在应用于所有测试,则fixture可以将其移动到conftest.py文件中并使用with autouse=True选项。

设置捕获方法或禁用捕获

有两种pytest执行捕获的方法:

  • 文件描述符(FD)级别捕获(默认):将捕获对操作系统文件描述符1和2的所有写操作。
  • sys级别捕获:仅写入Python文件,sys.stdout 并且sys.stderr将被捕获​​。不捕获对文件描述符的写入。

您可以从命令行影响输出捕获机制:

pytest -s            # disable all capturing
pytest --capture=sys # replace sys.stdout/stderr with in-mem files
pytest --capture=fd  # also point filedescriptors 1 and 2 to temp file

内部pytest警告

pytestWarning

基类:UserWarning。

pytest发出的所有警告的基类。

pytestAssertRewriteWarning

基类:pytestWarning。

pytest断言重写模块发出的警告。

pytestCacheWarning

基地:pytestWarning。

缓存插件在各种情况下发出的警告。

pytestCollectionWarning

基类:pytestWarning。

pytest无法在模块中收集文件或符号时发出警告。

pytestConfigWarning

基地:pytestWarning。

针对配置问题发出警告。

pytestDeprecationWarning

基类:pytest.pytestWarning,DeprecationWarning。

在将来的版本中将删除的功能的警告类。

pytestExperimentalApiWarning

基类:pytest.pytestWarning,FutureWarning。

警告类别,用于表示pytest中的实验。请谨慎使用,因为API可能会更改,甚至在将来的版本中会完全删除。

pytestUnhandledCoroutineWarning

基类:pytestWarning

当pytest遇到作为协程的测试函数时发出警告,但任何异步感知插件均未处理该警告。本机不支持协程测试功能。

pytestUnknownMarkWarning

基类:pytestWarning。

使用未知标记会发出警告。

        doctest

就像普通的一样conftest.py,fixtures是在目录树conftest中发现的。这意味着,如果将doctest与源代码一起放入,则相关的conftest.py需要位于同一目录树中。fixtures不会在同级目录树中发现!

        跳过和xfail

一个xfail意味着你期望测试失败的某些原因。一个常见的示例是对尚未实现的功能或尚未修复的错误进行测试。如果尽管测试通过但预期会失败(标记为pytest.mark.xfail),则为xpass,并将在测试摘要中报告。

pytest分别统计和列出跳过xfail测试。默认情况下,不显示有关跳过/未通过测试的详细信息,以避免混乱输出。

pytest -rxXs  # show extra info on xfailed, xpassed, and skipped tests

如何在不同情况下跳过模块中的测试的快速指南:

1)无条件跳过模块中的所有测试:

pytestmark = pytest.mark.skip("all tests still WIP")

2)根据某些条件跳过模块中的所有测试:

pytestmark = pytest.mark.skipif(sys.platform == "win32", reason="tests for linux only")

3)如果缺少某些导入,请跳过模块中的所有测试:

pexpect = pytest.importorskip("pexpect")

跨测试运行

该插件提供了两个命令行选项,以从上次pytest调用重新运行失败:

  • --lf,--last-failed-仅重新运行失败。
  • --ff,--failed-first-先运行故障,然后测试的其余部分

最后一次运行没有测试失败,或者找不到缓存的lastfailed数据,

pytest则可以使用以下--last-failed-no-failures选项之一将该选项配置为运行所有测试或不运行测试:

pytest --last-failed --last-failed-no-failures all    # run all tests (default behavior)

pytest --last-failed --last-failed-no-failures none   # run no tests and exit

pytest支持unittest开箱即用地运行基于Python 的测试

到目前为止,pytest不支持以下功能:

  • load_tests协议 ;
  • 子测试 
  1. 具有在给定上下文中自动使用的fixture,@pytest.fixture(autouse=True) 
  2. xunit样式: 在每个模块/类/功能的基础上实现fixture

Module setup/teardown

def setup_module(module):

           """ setup any state specific to the execution of the given module."""

def teardown_module(module):

""" teardown any state that was previously setup with a setup_module

    method.

    """

 

Class setup/teardown

@classmethod

def setup_class(cls):

""" setup any state specific to the execution of the given class (which

    usually contains tests).

    """

@classmethod

def teardown_class(cls):

""" teardown any state that was previously setup with a call to

    setup_class.

    """

 

方法和功能的setup/teardown

def setup_method(self,method):

""" setup any state tied to the execution of the given method in a

    class.  setup_method is invoked for every test method of a class.

    """

def teardown_method(self,method):

      """ teardown any state that was previously setup with a setup_method

    call.

    """

 

def setup_function(function):

     """ setup any state tied to the execution of the given function.

    Invoked for every test function in the module.

    """

def teardown_function(function):

""" teardown any state that was previously setup with a setup_function call.

    """

       工具启动时插件发现顺序

通常最好将conftest.py文件保存在顶级测试或项目根目录中。

典型setup.py摘录:

setup(..., entry_points={"pytest11": ["foo = pytest_foo.plugin"]}, ...)

通过名字来访问另一个插件

插件想要与另一个插件的代码协作,则可以通过插件管理器获取引用

plugin = config.pluginmanager.get_plugin("name_of_plugin")

pytest API

caplog()

访问和控制日志捕获。

捕获的日志可通过以下属性/方法获得:

* caplog.messages        -> list of format-interpolated log messages

* caplog.text            -> string containing formatted log output

* caplog.records         -> list of logging.LogRecord instances

* caplog.record_tuples   -> list of (logger_name, level, message) tuples

* caplog.clear()         -> clear captured records and formatted log output string

pip用于安装应用程序和所有依赖项以及pytest程序包本身。这样可确保您的代码和依赖项与系统Python安装隔离。

pytest.approx

失败的视频/屏幕截图pytest-splinter、pytest-bdd

考虑以下文件和目录布局:

root/

|- foo/

   |- __init__.py

   |- conftest.py

   |- bar/

      |- __init__.py

      |- tests/

         |- __init__.py

         |- test_foo.py

执行时会执行以下所有的目录文件

pytest root/

测试模块名称不能相同

从中找到rootdir的算法args:

  • 确定指定的公共祖先目录,这些目录args被识别为文件系统中存在的路径。如果找不到此类路径,则将公共祖先目录设置为当前工作目录。
  • 寻找pytest.ini,tox.ini并setup.cfg在父目录和文件向上。如果匹配,它将成为ini文件,并且其目录将成为rootdir。
  • 如果未找到ini文件,请setup.py从公共祖先目录向上查找以确定rootdir。
  • 如果没有setup.py被发现,寻找pytest.ini,tox.ini并 setup.cfg在每个指定args向上。如果匹配,它将成为ini文件,并且其目录将成为rootdir。
  • 如果找不到ini文件,则使用已经确定的公共祖先作为根目录。这允许在不属于包且没有任何特定ini文件配置的结构中使用pytest。

pytest实际运用

命令行中执行

pip install pytest

pip show pytest

 

想要在Pycharm环境中测试用例以pytest形式运行,可以这样设置。Settings->Tools->Python Integreated Tools,选择默认的test runner为“py.test”。

 

pytest生成html报告,命令行中安装pytest-html插件。

pip install pytest-html

 

cmd中执行 >pytest test_***.py --html=./testreport.html

 

在报告存放路径下,可以用Chrome浏览器打开本地html链接,如file:///E:/ATS/Test_doctor/testreport.html。

pytest测试报告形式如下所示

 

pytest失败重跑机制。在UI自动化测试中,由于受到网络不稳定、appium server等影响,不是每次正确的测试用例都能运行通过,于是使用pytest的失败重跑提高自动化测试的稳定性。安装插件pytest-rerunsfailures,在命令行执行

pip install pytest-rerunsfailures

 

>pytest test_patlist.py --reruns 3 --html=./report.html

实现对测试用例重跑3次,3次不通过才算失败,反之则运行成功。

实践得到,pytest不需要创建类,直接定义函数即可。

pytest 和unittest 最大的区别是不要求我们必须创建测试类, 自己创建的测试类也不需要继承于unittest.TestCase类。但是pytest 要求我们创建的测试文件,测试类、方法、测试函数必须以“test”开头,pytest默认按照这个规则查找测试用例并执行它们。”

 

pytest避开的坑

1)PytestUnknownMarkWarning

解决方案:

若是单个标签

在conftest.py添加如下代码,直接拷贝过去,把标签名改成你自己的就行了

def pytest_configure(config):
config.addinivalue_line(
"markers", "login_success" # login_success 是标签名
)

若是多个标签

在conftest.py添加如下代码,直接拷贝过去,把标签名改成你自己的就行了

def pytest_configure(config):
marker_list = ["testmark1","testmark2","testmark3"]  # 标签名集合
for markers in marker_list:
config.addinivalue_line(
"markers", markers
)

这样添加之后,再次运行,不再出现warning。

 

2)UnicodeDecodeError: 'gbk' codec can't decode byte 0xae in position 42: illegal m

ultibyte sequence

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.ini文件,内容是这样:

[pytest]

doctest_encoding = UTF-8  #默认编码是UTF-8

删除pytest.ini文件之后,再次运行就可以了。

3)  pytest执行用例时collected 0 items

 

pytest执行的文件需要以test开头才能被查找到。

4)断言assert

断言元素是否存在,例如

element = appdriver.find_element_by_id('icon_id')

assert element

标签:username,setup,py,pytest,测试,自动化,test,def
来源: https://www.cnblogs.com/fengye151/p/12901168.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有