pytest - 使用pytest过程中的5大超级技巧(实例详解篇)

从简单的断言和测试用例组织到更先进的参数化和夹具管理,pytest提供了强大的功能和灵活性。让我们一起探索这些技巧,使你的测试变得更加高效精准!

无需担心阅读时间过长,本文已经为您准备了详尽的解析和实际示例。立即开始,您将发现pytest的强大之处!

1、assert的各种使用场景

包含了几十种断言失败的各种场景,涉及断言的,可以查看pytest文档的 5.1.1(以 PDF 格式下载最新版本),举例几个常用的断言:

@pytest.mark.parametrize("param1, param2", [(3, 6)])
def test_generative(param1, param2):
    assert param1 * 2 < param2
    assert (3 * 2) < 6
    
def test_simple(self):
	def f():
		return 42
	def g():
		return 43
	assert f() == g()
	assert 42 == 43
	
def test_eq_long_text(self):
	a = "1" * 100 + "a" + "2" * 100
	b = "1" * 100 + "b" + "2" * 100
	assert a == b

2、基本模式和示例

2.1 更改命令行选项的默认值

# content of pytest.ini
[pytest]
addopts = -ra -q 

2.2 构建一个依赖于命令行选项的测试

根据命令行选项将不同的值传递给测试函数,运行用例:pytest -q test_sample.py --cmdopt=type3

# content of conftest.py
import pytest

def type_checker(value):
    msg = "cmdopt must specify a numeric type as typeNNN"
    if not value.startswith("type"):
        raise pytest.UsageError(msg)
    try:
        int(value[4:])
    except ValueError:
        raise pytest.UsageError(msg)
    return value


def pytest_addoption(parser):
    parser.addoption(
        "--cmdopt",
        action="store",
        default="type1",
        help="my option: type1 or type2",
        choices=("type1", "type2"),
        type=type_checker,
    )


@pytest.fixture
def cmdopt(request):
    return request.config.getoption("--cmdopt")

# content of test_sample.py

def test_answer(cmdopt):
    if cmdopt == "type1":
        print("first")
    elif cmdopt == "type2":
        print("second")
    assert 0

2.3 动态地添加命令行选项

# setuptools plugin
import sys
def pytest_load_initial_conftests(args):
	if "xdist" in sys.modules: # pytest-xdist plugin
		import multiprocessing
		
		num = max(multiprocessing.cpu_count() / 2, 1)
		args[:] = ["-n", str(num)] + args

2.4 控制跳过测试根据命令行选项

# content of conftest.py
import pytest
def pytest_addoption(parser):
	parser.addoption("--runslow", action="store_true", default=False, help="run slow tests" )
def pytest_configure(config):
	config.addinivalue_line("markers", "slow: mark test as slow to run")
def pytest_collection_modifyitems(config, items):
	if config.getoption("--runslow"):
		# --runslow given in cli: do not skip slow tests
		return
	skip_slow = pytest.mark.skip(reason="need --runslow option to run")
	for item in items:
		if "slow" in item.keywords:
			item.add_marker(skip_slow)

# content of test_module.py
import pytest
def test_func_fast():
	pass
@pytest.mark.slow
def test_func_slow():
	pass

2.5 编写良好的断言程序

# content of test_checkconfig.py
import pytest
def checkconfig(x):
	__tracebackhide__ = True
	if not hasattr(x, "config"):
		pytest.fail("not configured: {}".format(x))
def test_something():
	checkconfig(42)

2.6 检测是否从 pytest 运行中运行

# content of your_module.py
_called_from_test = False

# content of conftest.py
def pytest_configure(config):
	your_module._called_from_test = True

2.7 向测试报告题头添加信息

# content of conftest.py
def pytest_report_header(config):
	return "project deps: mylib-1.1"

2.8 分析测试持续时间

pytest --durations=3 test_some_are_slow.py

# content of test_some_are_slow.py
import time
def test_funcfast():
	time.sleep(0.1)
def test_funcslow1():
	time.sleep(0.2)
def test_funcslow2():
	time.sleep(0.3)

2.11 处理过程后的测试报告/失败

# content of conftest.py
import pytest
import os.path
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
	# execute all other hooks to obtain the report object
	outcome = yield
	rep = outcome.get_result()
	# we only look at actual failing test calls, not setup/teardown
	if rep.when == "call" and rep.failed:
		mode = "a" if os.path.exists("failures") else "w"
		with open("failures", mode) as f:
			# let's also access a fixture for the fun of it
			if "tmp_path" in item.fixturenames:
				extra = " ({})".format(item.funcargs["tmp_path"])
			else:
				extra = "" f.write(rep.nodeid + extra + "\n")

# content of test_module.py
def test_fail1(tmp_path):
	assert 0
def test_fail2():
	assert 0

2.12 在固定设备中提供测试结果信息

# content of conftest.py
import pytest
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
	# execute all other hooks to obtain the report object
	outcome = yield
	rep = outcome.get_result()
	# set a report attribute for each phase of a call, which can
	# be "setup", "call", "teardown"
	setattr(item, "rep_" + rep.when, rep)
@pytest.fixture
def something(request):
	yield
	# request.node is an "item" because we use the default
	# "function" scope
	if request.node.rep_setup.failed:
		print("setting up a test failed!", request.node.nodeid)
	elif request.node.rep_setup.passed:
		if request.node.rep_call.failed:
			print("executing test failed", request.node.nodeid)

# content of test_module.py
import pytest
@pytest.fixture
def other():
	assert 0
def test_setup_fails(something, other):
	pass
def test_call_fails(something):
	assert 0
def test_fail2():
	assert 0

2.9 测试步骤的增量测试
2.10 包装/目录级固定装置(设置)
2.13 PYTEST_CURRENT_TEST环境变量
2.14 Freezing pytest

3、用例参数化

3.1 生成参数组合,取决于命令行

  • pytest -vs test_sample.py
  • pytest -vs test_sample.py --all
# content of conftest.py
def pytest_addoption(parser):
    parser.addoption("--all", action="store_true", help="run all combinations")
def pytest_generate_tests(metafunc):
    if "param1" in metafunc.fixturenames:
        if metafunc.config.getoption("all"):  # 如果在命令行中获取到了字符串all:pytest -vs test_sample.py --all
            end = 5
        else:  # 如果在命令什么也没有:pytest -vs test_sample.py
            end = 2
            print("2")
        metafunc.parametrize("param1", range(end))

# content of test_compute.py
def test_compute(param1):
    assert param1 < 4

3.2 参数中设置不同的测试ID

# content of test_time.py
from datetime import datetime, timedelta
import pytest
testdata = [
    (datetime(2001, 12, 12), datetime(2001, 12, 11), timedelta(1)),
    (datetime(2001, 12, 11), datetime(2001, 12, 12), timedelta(-1)),
]
@pytest.mark.parametrize("a,b,expected", testdata)
def test_timedistance_v0(a, b, expected):
    diff = a - b
    assert diff == expected
@pytest.mark.parametrize("a,b,expected", testdata, ids=["forward", "backward"])
def test_timedistance_v1(a, b, expected):
    diff = a - b
    assert diff == expected
def idfn(val):
    if isinstance(val, (datetime,)):
        # note this wouldn't show any hours/minutes/seconds
        return val.strftime("%Y%m%d")
@pytest.mark.parametrize("a,b,expected", testdata, ids=idfn)
def test_timedistance_v2(a, b, expected):
    diff = a - b
    assert diff == expected

@pytest.mark.parametrize(
    "a,b,expected",
    [
        pytest.param(datetime(2001, 12, 12), datetime(2001, 12, 11), timedelta(1), id="forward"),
        pytest.param(datetime(2001, 12, 11), datetime(2001, 12, 12), timedelta(-1), id="backward"),
    ],
)
def test_timedistance_v3(a, b, expected):
    diff = a - b
    assert diff == expected

pytest test_time.py --collect-only,--collect-only 用来显示测试ID

collected 8 items                                                                                                                                                                                             
<Package test>
  <Module test_time.py>
    <Function test_timedistance_v0[a0-b0-expected0]>
    <Function test_timedistance_v0[a1-b1-expected1]>
    <Function test_timedistance_v1[forward]>
    <Function test_timedistance_v1[backward]>
    <Function test_timedistance_v2[20011212-20011211-expected0]>
    <Function test_timedistance_v2[20011211-20011212-expected1]>
    <Function test_timedistance_v3[forward]>
    <Function test_timedistance_v3[backward]>

3.3 “测试场景”的快速移植

# content of test_scenarios.py

def pytest_generate_tests(metafunc):
    idlist = []
    argvalues = []
    for scenario in metafunc.cls.scenarios:
        idlist.append(scenario[0])
        items = scenario[1].items()
        argnames = [x[0] for x in items]
        argvalues.append([x[1] for x in items])
    metafunc.parametrize(argnames, argvalues, ids=idlist, scope="class")
    
scenario1 = ("basic", {"attribute": "value"})
scenario2 = ("advanced", {"attribute": "value2"})

class TestSampleWithScenarios:
    scenarios = [scenario1, scenario2]

    def test_demo1(self, attribute):
        assert isinstance(attribute, str)

    def test_demo2(self, attribute):
        assert isinstance(attribute, str)

3.4 延迟对参数化资源的设置

3.5 间接参数化

在参数化测试时,使用indirect=True参数允许参数化测试之前参数化测试:

import pytest
@pytest.fixture
def fixt(request):
    return request.param * 3

@pytest.mark.parametrize("fixt", ["a", "b"], indirect=True)
def test_indirect(fixt):
    assert len(fixt) == 3

3.6 间接地应用于特定的参数

@pytest.mark.parametrize中的值再次做参数化,使用indirect=["x"]指定引用固定装置X再次对值进行参数化处理:

# content of test_indirect_list.py
import pytest

@pytest.fixture(scope="function")
def x(request):
    return request.param * 3
@pytest.fixture(scope="function")
def y(request):
    return request.param * 2

# indirect给了值X:引用了上面的固定装置X,没给Y,没引用固定装置Y,所以Y的取值是b
@pytest.mark.parametrize("x, y", [("a", "b")], indirect=["x"])
def test_indirect(x, y):
    assert x == "aaa"
    assert y == "b"

3.7 通过每个类的配置来参数化测试方法

这里是一个示例pytest_generate_tests函数实现一个参数化方案

# content of ./test_parametrize.py
import pytest
def pytest_generate_tests(metafunc):
    # called once per each test function
    funcarglist = metafunc.cls.params[metafunc.function.__name__]
    argnames = sorted(funcarglist[0])
    metafunc.parametrize(argnames, [[funcargs[name] for name in argnames] for funcargs in funcarglist])
class TestClass:
    # a map specifying multiple argument sets for a test method
    params = { "test_equals": [dict(a=1, b=2), dict(a=3, b=3)], "test_zerodivision": [dict(a=1, b=0)],}
    def test_equals(self, a, b):
        assert a == b
    def test_zerodivision(self, a, b):
        with pytest.raises(ZeroDivisionError):
            a / b

3.8 使用多种固定装置的间接参数化

import shutil
import subprocess
import textwrap
import pytest

pythonlist = ["python3.5", "python3.6", "python3.7", "python3.9.6"]


@pytest.fixture(params=pythonlist)
def python1(request, tmp_path):
    picklefile = tmp_path / "data.pickle"
    return Python(request.param, picklefile)


@pytest.fixture(params=pythonlist)
def python2(request, python1):
    return Python(request.param, python1.picklefile)


class Python:
    def __init__(self, version, picklefile):
        self.pythonpath = shutil.which(version)
        if not self.pythonpath:
            pytest.skip(f"{version!r} not found")
        self.picklefile = picklefile

    def dumps(self, obj):
        dumpfile = self.picklefile.with_name("dump.py")
        dumpfile.write_text(textwrap.dedent(
                r"""
                import pickle
                f = open({!r}, 'wb')
                s = pickle.dump({!r}, f, protocol=2)
                f.close()
                """.format(str(self.picklefile), obj)
        ))
        subprocess.check_call((self.pythonpath, str(dumpfile)))

    def load_and_is_true(self, expression):
        loadfile = self.picklefile.with_name("load.py")
        loadfile.write_text(
            textwrap.dedent(
                r"""
                import pickle
                f = open({!r}, 'rb')
                obj = pickle.load(f)
                f.close()
                res = eval({!r})
                if not res:
                raise SystemExit(1)
                """.format(
                    str(self.picklefile), expression
                )))
        print(loadfile)
        subprocess.check_call((self.pythonpath, str(loadfile)))

@pytest.mark.parametrize("obj", [42, {}, {1: 3}])
def test_basic_objects(python1, python2, obj):
    python1.dumps(obj)
    python2.load_and_is_true(f"obj == {obj}")

3.9 可选实现/导入的间接参数化

3.10 为个别参数化测试设置标记或测试ID

# content of test_pytest_param_example.py
import pytest
@pytest.mark.parametrize(
    "test_input,expected",
    [
        ("3+5", 8),
        pytest.param("1+7", 8, marks=pytest.mark.basic),
        pytest.param("2+4", 6, marks=pytest.mark.basic, id="basic_2+4"),
        pytest.param("6*9", 42, marks=[pytest.mark.basic, pytest.mark.xfail], id="basic_6*9"),
    ],
)
def test_eval(test_input, expected):
    assert eval(test_input) == expected

3.11 参数化条件提升

from contextlib import contextmanager
import pytest
@contextmanager
def does_not_raise():
    yield
    
@pytest.mark.parametrize(
    "example_input,expectation",
    [
        (3, does_not_raise()),
        (2, does_not_raise()),
        (1, does_not_raise()),
        (0, pytest.raises(ZeroDivisionError)),
    ],
)
def test_division(example_input, expectation):
    """Test how much I know division."""
    with expectation:
        assert (6 / example_input) is not None

4、自定义标记

4.1 标记测试函数并运行

  • pytest test_module.py -m webtest
  • pytest test_module.py -v -m "not webtest
# content of test_server.py
import pytest
@pytest.mark.webtest
def test_send_http():
    pass # perform some webtest test for your app
def test_something_quick():
    pass
def test_another():
    pass
class TestClass:
    def test_method(self):
        pass

4.2 根据节点选择测试

  • pytest -v test_server.py::TestClass
  • pytest -v test_server.py::TestClass::test_method
  • pytest -v test_server.py::TestClass test_server.py::test_send_http

4.3 根据正则表达式选择测试用例

  • pytest -v -k http test_server.py
  • pytest -v -k "not send_http" test_server.py
  • pytest -v -k "http or quick" test_server.py
# content of test_server.py
import pytest
@pytest.mark.webtest
def test_send_http():
    pass # perform some webtest test for your app
def test_something_quick():
    pass
def test_another():
    pass
class TestClass:
    def test_method(self):
        pass

4.4 注册标记

为测试套件注册标记很简单,查看注册的标记:pytest --markers

# content of pytest.ini
[pytest]
markers =
	webtest: mark a test as a webtest.
	slow: mark test as slow.

4.5 标记整个类或模块

# content of test_mark_classlevel.py
import pytest
@pytest.mark.webtest
class TestClass:
	def test_startup(self):
		pass
	def test_startup_and_more(self):
		pass

要在模块级别应用标记,也可以使用全局变量定义:

import pytest
# 单个标记
pytestmark = pytest.mark.webtest
# 多个标记:
pytestmark = [pytest.mark.webtest, pytest.mark.slowtest]
class TestClass:
	def test_startup(self):
		pass
	def test_startup_and_more(self):
		pass
import pytest
class TestClass:
	pytestmark = pytest.mark.webtest
	def test_startup(self):
		pass

4.6 参数化中标记各个测试

import pytest

@pytest.mark.foo
@pytest.mark.parametrize(("n", "expected"), [(1, 2), pytest.param(1, 3, marks=pytest.mark.bar), (2, 3)])
def test_increment(n, expected):
    assert n + 1 == expected

4.7 自定义标记和命令行选项

pytest -E stage2 test_moule.py

# content of conftest.py
import pytest
def pytest_addoption(parser):
    parser.addoption(
    "-E",
    action="store",
    metavar="NAME",
    help="only run tests matching the environment NAME.", )
def pytest_configure(config):
    # register an additional marker
    config.addinivalue_line(
    "markers", "env(name): mark test to run only on named environment" )
def pytest_runtest_setup(item):
    envnames = [mark.args[0] for mark in item.iter_markers(name="env")]
    if envnames:
        if item.config.getoption("-E") not in envnames:
            pytest.skip(f"test requires env in {envnames!r}")
# content of test_someenv.py
import pytest
@pytest.mark.env("stage1")
def test_basic_db_operation():
	pass

4.8 从多个地方读取标记

# content of test_mark_three_times.py
import pytest
pytestmark = pytest.mark.glob("module", x=1)
@pytest.mark.glob("class", x=2)
class TestClass:
	@pytest.mark.glob("function", x=3)
	def test_something(self):
		pass
# content of conftest.py`在这里插入代码片`
import sys
def pytest_runtest_setup(item):
	for mark in item.iter_markers(name="glob"):
		print(f"glob args={mark.args} kwargs={mark.kargs}")
		sys.stdout.flush()

4.9 标记不同的测试平台环境

pytest -m linux test_plat.py

# content of conftest.py
#
import sys
import pytest
ALL = set("darwin linux win32".split())
def pytest_runtest_setup(item):
	supported_platforms = ALL.intersection(mark.name for mark in item.iter_markers())
	plat = sys.platform
	if supported_platforms and plat not in supported_platforms:
		pytest.skip(f"cannot run on platform {plat}")
# content of test_plat.py
import pytest
@pytest.mark.darwin
def test_if_apple_is_evil():
	pass
@pytest.mark.linux
def test_if_linux_works():
	pass
@pytest.mark.win32
def test_if_win32_crashes():
	pass
def test_runs_everywhere():
	pass

4.10 基于测试名称自动添加标记

  • pytest -m interface --tb=short test_moudle.py
  • pytest -m "interface or event" --tb=short test_moudle.py
# content of test_module.py
def test_interface_simple():
	assert 0
def test_interface_complex():
	assert 0
def test_event_simple():
	assert 0
def test_something_else():
	assert 0
# content of conftest.py
import pytest
def pytest_collection_modifyitems(items):
	for item in items:
	if "interface" in item.nodeid:
		item.add_marker(pytest.mark.interface)
	elif "event" in item.nodeid:
		item.add_marker(pytest.mark.event)

5、session级别的固定装置

会话范围的夹具有效地访问所有收集的测试项目。 这是一个fixture函数的例子 遍历所有收集的测试并查看他们的测试类是否定义了 callme 方法并调用它:

# content of conftest.py
import pytest
@pytest.fixture(scope="session", autouse=True)
def callattr_ahead_of_alltests(request):
    print("callattr_ahead_of_alltests called")
    seen = {None}
    session = request.node
    for item in session.items:
        cls = item.getparent(pytest.Class)
        if cls not in seen:
            if hasattr(cls.obj, "callme"):
                cls.obj.callme()
                seen.add(cls)
# content of test_module.py
class TestHello:
    @classmethod
    def callme(cls):
        print("callme called!")
    def test_method1(self):
        print("test_method1 called")
    def test_method2(self):
        print("test_method2 called")
class TestOther:
    @classmethod
    def callme(cls):
        print("callme other called")
    def test_other(self):
        print("test other")
# works with unittest as well ...
import unittest
class SomeTest(unittest.TestCase):
    @classmethod
    def callme(self):
        print("SomeTest callme called")
    def test_unit1(self):
        print("test_unit1 method called")
C:\Users\mc\Desktop\python>pytest -q -s test_module.py
callattr_ahead_of_alltests called
callme called!
callme other called
SomeTest callme called
test_method1 called
.test_method2 called
.test other
.test_unit1 method called

6、更改标准 (Python) 发现测试用例

6.1 在测试收集期间忽略路径: --ignore

  • 通过传递 --ignore=path 选项,您可以在收集过程中轻松忽略某些测试目录和模块cli,pytest 允许多个 --ignore 选项。 例如test目录上执行:
    pytest --ignore=tests/foobar/test_foobar_03.py --ignore=tests/hello/
  • --ignore-glob 选项允许忽略基于 Unix shell 样式通配符的测试文件路径。 如果要排除以 _01.py 结尾的测试模块:
    pytest --ignore-glob='*_01.py'
tests/
|-- example
|  |-- test_example_01.py
|  |-- test_example_02.py
|  |-- test_example_03.py
|-- foobar
|  |-- test_foobar_01.py
|  |-- test_foobar_02.py
|  |-- test_foobar_03.py
|-- hello
	|-- world
		|-- test_world_01.py
		|-- test_world_02.py
		|-- test_world_03.py

6.2 在测试收集期间忽略测试用例:--deselect

pytest --deselect tests/foobar/test_foobar_01.py::test_a

6.3 更改目录递归

告诉 pytest 不要递归到典型的 subversion 或 sphinx-build 目录或任何以 tmp 为前缀的目录。

# content of pytest.ini
[pytest]
norecursedirs = .svn _build tmp*

6.4 更改命名约定

在你的配置文件中,您可以通过设置 python_files、python_classes 和 python_functions :pytest --collect-only

# content of pytest.ini
# Example 1: have pytest look for "check" instead of "test"
[pytest]
python_files = check_*.py test_*.py example_*.py
python_classes = Check
python_functions = *_check

# content of check_myapp.py
class CheckMyApp:
	def simple_check(self):
		pass
	def complex_check(self):
		pass

注意:对unittest发现用例的方法不管用,因为pytest将测试用例方法的发现委托给unittest

6.5 查看测试用例树

pytest --collect-only test_module.py

# content of test_module.py
class TestHello:
    @classmethod
    def callme(cls):
        print("callme called!")
    def test_method1(self):
        print("test_method1 called")
    def test_method2(self):
        print("test_method2 called")
class TestOther:
    @classmethod
    def callme(cls):
        print("callme other called")
    def test_other(self):
        print("test other")
# works with unittest as well ...
import unittest
class SomeTest(unittest.TestCase):
    @classmethod
    def callme(self):
        print("SomeTest callme called")
    def test_unit1(self):
        print("test_unit1 method called")

C:\Users\mc\Desktop\python基础>pytest --collect-only test_module.py
======================================================================= test session starts ========================================================================
platform win32 -- Python 3.9.6, pytest-7.1.1, pluggy-0.13.1
rootdir: C:\Users\mc\Desktop\python基础
collected 4 items                                                                                                                                                   

<Module test_module.py>
  <Class TestHello>
    <Function test_method1>
    <Function test_method2>
  <Class TestOther>
    <Function test_other>
  <UnitTestCase SomeTest>
    <TestCaseFunction test_unit1>

==================================================================== 4 tests collected in 0.03s ====================================================================

6.6 忽略收集的某些文件

然而,许多项目会有一个他们不想被导入的 setup.py。 此外,可能只有文件可由特定的 python 版本导入。 对于这种情况,您可以通过将它们列出来动态定义要忽略的文件一个 conftest.py 文件

# content of pytest.ini
[pytest]
python_files = *.py

# content of conftest.py
import sys
collect_ignore = ["setup.py"]
if sys.version_info[0] > 2:
	collect_ignore.append("pkg/module_py2.py")

# content of pkg/module_py2.py
def test_only_on_python2():
try:
	assert 0
except Exception, e:
	pass

7、使用非python进行测试

7.1 在Yaml文件中指定测试的基本示例

这是一个示例 conftest.py( pytest-yamlwsgi 插件),conftest.py 将收集 test*.yaml 文件并将 yaml 格式的内容作为自定义测试执行:pytest -vs test_simple.yaml

 
 
collected 2 items
test_simple.yaml::hello FAILED
test_simple.yaml::ok PASSED

# content of conftest.py
import pytest
def pytest_collect_file(parent, file_path):
    if file_path.suffix == ".yaml" and file_path.name.startswith("test"):
        return YamlFile.from_parent(parent, path=file_path)
class YamlFile(pytest.File):
    def collect(self):
    # We need a yaml parser, e.g. PyYAML.
        import yaml
        raw = yaml.safe_load(self.path.open())
        for name, spec in sorted(raw.items()):
            yield YamlItem.from_parent(self, name=name, spec=spec)
class YamlItem(pytest.Item):
    def __init__(self, *, spec, **kwargs):
        super().__init__(**kwargs)
        self.spec = spec
    def runtest(self):
        for name, value in sorted(self.spec.items()):
        # Some custom test execution (dumb example follows).
            if name != value:
                raise YamlException(self, name, value)
    def repr_failure(self, excinfo):
        """Called when self.runtest() raises an exception."""
        if isinstance(excinfo.value, YamlException):
            return "\n".join(
            [ "usecase execution failed", " spec failed: {1!r}: {2!r}".format(*excinfo.value.args),
            " no further details known at this point.", ] )
    def reportinfo(self):
        return self.path, 0, f"usecase: {self.name}"

class YamlException(Exception):
    """Custom exception for error reporting."""

# test_simple.yaml
ok:
  sub1: sub1
hello:
  world: world
  some: other

  

结语

这篇贴子到这里就结束了,最后,希望看这篇帖子的朋友能够有所收获。

 获取方式:留言【软件测试学习】即可

如果你觉得文章还不错,请大家 点赞、分享、留言 下,因为这将是我持续输出更多优质文章的最强动力!

猜你喜欢

转载自blog.csdn.net/weixin_67553250/article/details/131288717
今日推荐