python 重写断言_Python自动化框架之pytest,从入门到精通知识点全收录

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

一、Pytest基本知识

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

1、Pytest中可以按节点ID运行测试。

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

pytest test_mod.py::TestClass::test_method

2、通过标记表达式运行测试

pytest -m slow

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

3、详细的总结报告

pytest –ra

4、分析测试执行持续时间

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

pytest --durations=10

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

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

pytest --pastebin=failed

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

pytest --pastebin=all

6、从python代码中调用Pytest

pytest.main()

注意:

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

7、断言

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

8、conftest.py

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

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

10、使用直接测试参数化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'

11、使用非参数化的参数覆盖参数化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'

12、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选项。

13、设置捕获方法或禁用捕获

有两种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

14、内部Pytest警告

(1)类PytestWarning

基类:UserWarning。

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

(2)类PytestAssertRewriteWarning

基类:PytestWarning。

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

(3)类PytestCacheWarning

基地:PytestWarning。

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

(4)类PytestCollectionWarning

基类:PytestWarning。

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

(5)类PytestConfigWarning

基地:PytestWarning。

针对配置问题发出警告。

(6)类PytestDeprecationWarning

基类:Pytest.PytestWarning,DeprecationWarning。

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

(7)类PytestExperimentalApiWarning

基类:Pytest.PytestWarning,FutureWarning。

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

(8)类PytestUnhandledCoroutineWarning

基类:PytestWarning。

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

(9)类PytestUnknownMarkWarning

基类:PytestWarning。

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

1)doctest

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

2)跳过和xfail

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

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

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

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

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")

18、跨测试运行

该插件提供了两个命令行选项,以从上次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

19、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.

"""

3)工具启动时插件发现顺序

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

20、典型setup.py摘录:

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

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

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

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

22、Pytest API

23、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

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

25、Pytest.approx

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

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

root/

|- foo/

|- __init__.py

|- conftest.py

|- bar/

|- __init__.py

|- tests/

|- __init__.py

|- test_foo.py

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

pytest root/

28、测试模块名称不能相同

29、从中找到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 multibyte 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

我是谁?

我是一名从事了多年软件测试的老测试员,今年年初我花了一个月整理了一份最适合2020年学习的软件测试学习干货,可以送给每一位对软件测试感兴趣的小伙伴,想要获取的可以关注我的头条号并在后台私信我:【测试】,即可免费获取。

你可能感兴趣的:(python,重写断言)