做网站每年需要购买域名吗,顺义建设工程交易中心网站,哪个网站做民宿更好呢,wordpress人力资源模板下载开始使用 Python 进行测试可能十分困难。Python 的标准库确实提供了一些用于编写测试的实用程序和帮助程序#xff0c;但也有一些缺点可能会导致编写比较困难。
Pytest 是最常用的 Python 测试工具和框架之一。尽管 Pytest 可以帮助实现高度复杂的测试方案#xff0c;但在创…开始使用 Python 进行测试可能十分困难。Python 的标准库确实提供了一些用于编写测试的实用程序和帮助程序但也有一些缺点可能会导致编写比较困难。Pytest 是最常用的 Python 测试工具和框架之一。尽管 Pytest 可以帮助实现高度复杂的测试方案但在创建测试时它不会强制使用其功能。你可以编写简单的测试但仍受益于快速且功能齐全的测试运行程序以及有用的报告。Pytest 的一个关键点在于利用它可以更轻松地编写测试。可以编写一个没有依赖项或配置的测试函数并立即运行测试。我们在此介绍 Pytest 入门所需的一些基础知识以便你可以将自己的测试套件提升到新的水平。要学习的知识完成本模块后能够使用 Pytest 编写测试解释其丰富的故障报告并利用其功能齐全的测试运行程序。应该可以轻松使用测试函数和类并且能够确定何时使用哪一个。有了这些知识后你可以使用 Pytest 的现有测试或扩展现有的测试套件。使用 Pytest 的错误报告准确确定故障。使用 Pytest 作为库和命令行工具。主要目标是什么你应该可以轻松使用 Pytest 和编写 Pytest 测试在我们的帮助下编写更多更好的测试成为更高效的工程师。01Pytest基础知识让我们开始使用 Pytest 进行测试。如上面所述Pytest 高度可配置可处理复杂的测试套件。但是开始编写测试时不需要用到太多。事实上编写测试的框架越简单越好。在本部分结束时应拥有开始编写第一个测试并使用 Pytest 运行它们所需的一切内容。约定在深入了解编写测试之前我们必须了解 Pytest 所依赖的一些测试约定。在 Python 中没有关于测试文件、测试目录或常规测试布局的硬性规则。你可以通过了解这些规则使用自动测试发现和执行而无需进行任何其他配置。测试目录和测试文件测试的主目录是 tests 目录。可以将此目录置于项目的根级别但有人也经常把它与代码模块放在一起。备注在本模块中我们默认使用项目根目录中的 tests。让我们看看名为 jformat 的小型 Python 项目的根目录是怎样的.├── README.md├── jformat│ ├── __init__.py│ └── main.py├── setup.py└── tests└── test_main.pytests 目录位于具有一个测试文件的项目的根目录中。在这种情况下此测试文件称为 test_main.py。此示例演示了两个关键约定使用 tests 目录来放置测试文件和嵌套的测试目录。测试文件的前缀为 test。此前缀指示文件包含测试代码。注意避免将 test单一形式用作目录名称。test 名称是一个 Python 模块因此创建一个与之同名的目录将会替代它。请始终改用复数形式的 tests。测试函数使用 Pytest 的一个有力论据是它允许编写测试函数。与测试文件类似测试函数必须带有 test_ 前缀。test_ 前缀可确保 Pytest 收集并执行测试。一个简单的测试函数如下所示def test_main():assert a string value a string value备注如果你熟悉使用 unittest则在测试函数中使用 assert 可能会令人惊喜。稍后我们会更详细地介绍普通断言但使用 Pytest你将通过普通断言获得丰富的失败报告。测试类和测试方法与文件和函数的约定类似测试类和测试方法使用以下约定测试类的前缀为 Test测试方法的前缀为 test_与 Python 的 unittest 库的核心区别在于无需继承。以下示例对类和方法使用这些前缀和其他 Python 命名约定。它演示了一个小型测试类该类在检查应用程序中的用户名。class TestUser:def test_username(self):assert default() default username运行测试Pytest 既是测试框架也是测试运行程序。测试运行程序是命令行中的可执行文件在高级别可以通过查找测试运行的所有测试文件、测试类和测试函数来执行测试集合。通过执行所有测试来启动测试运行。跟踪测试失败、测试错误和测试通过的信息。在测试运行结束时提供丰富的报告。备注由于 Pytest 是外部库因此必须安装后才能使用。鉴于 test_main.py 文件中的这些内容我们可以看到 Pytest 运行测试时的行为# contents of test_main.py filedef test_main():assert True在命令行中在 test_main.py 文件所在的同一路径中可以运行 pytest 可执行文件$ pytest test session starts platform -- Python 3.10.1, pytest-6.2.5, py-1.11.0, pluggy-1.0.0rootdir: /private/tmp/projectcollected 1 itemtest_main.py . [100%] 1 passed in 0.00s 在后台Pytest 可收集测试文件中的示例测试而无需进行任何配置。强大的断言语句到目前为止测试示例全部使用普通的 assert 调用。在 Python 中assert 语句不用于测试因为它在断言失败时缺少正确的报告。但是Pytest 没有此限制。在后台Pytest 使语句能够执行丰富的比较而无需强制用户编写更多代码或配置任何内容。通过使用普通的 assert 语句可以使用 Python 的运算符。例如、、!、 或 。所有 Python 运算符都是有效的。此功能可能是 Pytest 最关键的功能你不需要了解用于编写断言的新语法。让我们看看在处理与 Python 对象的常见比较时它是如何转换的。在本例中我们在比较长字符串时完成失败报告 FAILURES ____________________________ test_long_strings _____________________________def test_long_strings():left this is a very long strings to be compared with another long stringright This is a very long string to be compared with another long string assert left rightE AssertionError: assert this is a ve...r long string This is a ve...r long stringE - This is a very long string to be compared with another long stringE ? ^E this is a very long strings to be compared with another long stringE ? ^ test_main.py:4: AssertionErrorPytest 显示有关失败的有用上下文。字符串开头的大小写不正确单词中包含一个额外字符。但除字符串外Pytest 还可以帮助其他对象和数据结构。例如以下是它处理列表的方式________________________________ test_lists ________________________________def test_lists():left [sugar, wheat, coffee, salt, water, milk]right [sugar, coffee, wheat, salt, water, milk] assert left rightE AssertionError: assert [sugar, wh...ater, milk] [sugar, co...ater, milk]E At index 1 diff: wheat ! coffeeE Full diff:E - [sugar, coffee, wheat, salt, water, milk]E ? ---------E [sugar, wheat, coffee, salt, water, milk]E ? test_main.py:9: AssertionError此报告标识索引 1列表中的第二项有所不同。它不仅标识索引号还提供失败的表示形式。除了提供项比较之外它还可以报告项是否缺失并提供信息使你准确得知可能是哪一项。在以下情况中此项为 milk________________________________ test_lists ________________________________def test_lists():left [sugar, wheat, coffee, salt, water, milk]right [sugar, wheat, salt, water, milk] assert left rightE AssertionError: assert [sugar, wh...ater, milk] [sugar, wh...ater, milk]E At index 2 diff: coffee ! saltE Left contains one more item: milkE Full diff:E - [sugar, wheat, salt, water, milk]E [sugar, wheat, coffee, salt, water, milk]E ? test_main.py:9: AssertionError最后让我们看看它如何处理字典。如果存在失败则比较两个大字典可能会让人难以应对但 Pytest 在提供上下文并查明失败时会执行未完成的工作____________________________ test_dictionaries _____________________________def test_dictionaries():left {street: Ferry Ln., number: 39, state: Nevada, zipcode: 30877, county: Frett}right {street: Ferry Lane, number: 38, state: Nevada, zipcode: 30877, county: Frett} assert left rightE AssertionError: assert {county: F...rry Ln., ...} {county: F...ry Lane, ...}E Omitting 3 identical items, use -vv to showE Differing items:E {street: Ferry Ln.} ! {street: Ferry Lane}E {number: 39} ! {number: 38}E Full diff:E {E county: Frett,...EE ...Full output truncated (12 lines hidden), use -vv to show在此测试中字典中有两个失败。一个是 street 值不同另一个是 number 不匹配。Pytest 可以准确地检测到这些差异即使它是单个测试中的一个失败。由于字典包含许多项Pytest 省略相同的部分且只显示相关内容。如果我们使用建议的 -vv 标记来使输出内容更加详细让我们看看会发生什么情况____________________________ test_dictionaries _____________________________def test_dictionaries():left {street: Ferry Ln., number: 39, state: Nevada, zipcode: 30877, county: Frett}right {street: Ferry Lane, number: 38, state: Nevada, zipcode: 30877, county: Frett} assert left rightE AssertionError: assert {county: Frett,\n number: 39,\n state: Nevada,\n street: Ferry Ln.,\n zipcode: 30877} {county: Frett,\n number: 38,\n state: Nevada,\n street: Ferry Lane,\n zipcode: 30877}E Common items:E {county: Frett, state: Nevada, zipcode: 30877}E Differing items:E {number: 39} ! {number: 38}E {street: Ferry Ln.} ! {street: Ferry Lane}E Full diff:E {E county: Frett,E - number: 38,E ? ^E number: 39,E ? ^E state: Nevada,E - street: Ferry Lane,E ? - ^E street: Ferry Ln.,E ? ^E zipcode: 30877,E }通过运行 pytest -vv报告会增加详细信息量并提供精细比较。此报告不仅会检测并显示失败还支持快速进行更改以修正问题。02 测试类和方法使用 Pytest 除了能编写测试函数外还能使用类。正如我们已经提到的不需要继承测试类只遵循一些简单的规则。使用类可以提高灵活性和可重用性。正如你接下来会看到的Pytest 不会妨碍你并会避免强制你以某种方式编写测试。与函数一样仍然可以使用 assert 语句编写断言。生成测试类让我们使用一个实际场景来了解测试类如何提供帮助。以下函数检查给定文件在其内容中是否包含“yes”。如果包含则返回 True。如果文件不存在或在其内容中包含“no”则返回 False。这种情况在使用文件系统指示完成情况的异步任务中很常见。该函数如下所示import osdef is_done(path):if not os.path.exists(path):return Falsewith open(path) as _f:contents _f.read()if yes in contents.lower():return Trueelif no in contents.lower():return False下面是名为 test_files.py 的文件中的具有两个测试每个条件一个测试的类class TestIsDone:def test_yes(self):with open(/tmp/test_file, w) as _f:_f.write(yes)assert is_done(/tmp/test_file) is Truedef test_no(self):with open(/tmp/test_file, w) as _f:_f.write(no)assert is_done(/tmp/test_file) is False注意测试方法对临时测试文件使用 /tmp 路径因为它更容易用于示例。但是如果需要使用临时文件请考虑使用 tempfile 之类的库它可安全地创建和删除这些文件。并非所有系统都有 /tmp 目录并且该位置可能不是临时的具体取决于操作系统。若使用 -v 标志运行测试以增加详细程度可显示测试通过pytest -v test_files.py test session starts Python 3.9.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0cachedir: .pytest_cacherootdir: /private/collected 2 itemstest_files.py::TestIsDone::test_yes PASSED [ 50%]test_files.py::TestIsDone::test_no PASSED [100%] 2 passed in 0.00s 虽然通过了测试但它们看起来存在重复并且在测试完成后还会留下文件。让我们先在下一部分中了解帮助程序方法然后再了解如何改进测试。帮助器方法在测试类中可以使用几种方法来设置和拆解测试执行。如果定义了测试Pytest 会自动执行。若要使用这些方法应知道它们具有特定的顺序和行为。setup在类中的每个测试之前执行一次teardown在类中的每个测试之后执行一次setup_class在类中的所有测试之前执行一次teardown_class在类中的所有测试之后执行一次如果测试需要类似的或相同的资源才能正常工作时编写设置方法会很有用。理想情况下测试完成后不应留下资源因此在这些情况下拆解方法可以帮助进行测试清理。清理现在来看一个更新的测试类它会在每次测试后清理文件class TestIsDone:def teardown(self):if os.path.exists(/tmp/test_file):os.remove(/tmp/test_file)def test_yes(self):with open(/tmp/test_file, w) as _f:_f.write(yes)assert is_done(/tmp/test_file) is Truedef test_no(self):with open(/tmp/test_file, w) as _f:_f.write(no)assert is_done(/tmp/test_file) is False由于使用了 teardown() 方法所以此测试类不再遗留 /tmp/test_file。设置我们可以对此类执行的另一个改进是添加指向文件的变量。由于该文件现已在六个位置声明因此对路径的任何更改都意味着在所有这些位置更改它。此示例展示添加了声明路径变量的 setup() 方法后该类是什么样子class TestIsDone:def setup(self):self.tmp_file /tmp/test_filedef teardown(self):if os.path.exists(self.tmp_file):os.remove(self.tmp_file)def test_yes(self):with open(self.tmp_file, w) as _f:_f.write(yes)assert is_done(self.tmp_file) is Truedef test_no(self):with open(self.tmp_file, w) as _f:_f.write(no)assert is_done(self.tmp_file) is False自定义帮助程序方法可以在类中创建自定义帮助程序方法。这些方法不得以名称 test 为前缀并且不能命名为安装程序或清理方法。在 TestIsDone 类中我们可以在自定义帮助程序中自动创建临时文件。该自定义帮助程序方法可能如以下示例所示def write_tmp_file(self, content):with open(self.tmp_file, w) as _f:_f.write(content)AI写代码Pytest 不会自动执行 write_tmp_file() 方法其他方法可以直接调用该方法以保存重复的任务例如写入文件。将测试方法更新为使用自定义帮助程序后整个类如下例所示class TestIsDone:def setup(self):self.tmp_file /tmp/test_filedef teardown(self):if os.path.exists(self.tmp_file):os.remove(self.tmp_file)def write_tmp_file(self, content):with open(self.tmp_file, w) as _f:_f.write(content)def test_yes(self):self.write_tmp_file(yes)assert is_done(self.tmp_file) is Truedef test_no(self):self.write_tmp_file(no)assert is_done(self.tmp_file) is False何时使用类而不是函数关于何时使用类而不是函数没有任何严格的规则。一个好的做法是持续遵循项目和团队中一直使用的惯例做法。可通过下面这些常用问题来帮助确定何时使用类测试是否需要类似的设置或清理帮助程序代码将测试组合在一起是否有逻辑上的意义测试套件中是否至少有一些测试测试能否受益于常用的帮助程序函数03练习在本练习中使用 Pytest 测试函数。然后发现并修复函数当中导致测试失败的一些潜在问题。查看失败结果和使用 Pytest 丰富的错误报告功能对于发现和修复生产代码中存在问题的测试或 bug 至关重要。在本练习中我们使用称为 admin_command() 的函数该函数接受系统命令作为输入并可以使用 sudo 工具为其添加前缀可选操作。该函数有一个 bug你将通过编写测试来发现该 bug。步骤 1 - 为本练习添加测试文件1.使用测试文件的 Python 文件名约定创建一个新的测试文件。将测试文件命名为 test_exercise.py 并添加以下代码def admin_command(command, sudoTrue):Prefix a command with sudo unless it is explicitly not needed. Expectscommand to be a list.if sudo:[sudo] commandreturn command函数 admin_command() 使用 command 参数接受列表作为输入并且可以使用 sudo 为列表添加前缀可选操作。如果 sudo 关键字参数设置为 False则该参数会返回输入的命令。2.在同一个文件中追加对 admin_command() 函数的测试。测试使用返回示例命令的帮助程序方法class TestAdminCommand:def command(self):return [ps, aux]def test_no_sudo(self):result admin_command(self.command(), sudoFalse)assert result self.command()def test_sudo(self):result admin_command(self.command(), sudoTrue)expected [sudo] self.command()assert result expected备注在与实际代码相同的文件中进行测试并不常见。为简单起见本练习中的示例将具有同一文件中的实际代码。在实际 Python 项目中文件和目录通常会将测试与正在测试的代码分开。步骤 2 - 运行测试并确定失败由于测试文件有一个要测试的函数和几个要验证其行为的测试现在是时候运行测试和处理失败了。使用 Python 执行文件$ pytest test_exercise.py运行应以一项测试通过和一项失败完成失败输出应类似于以下输出 FAILURES __________________________ TestAdminCommand.test_sudo __________________________self test_exercise.TestAdminCommand object at 0x10634c2e0def test_sudo(self):result admin_command(self.command(), sudoTrue)expected [sudo] self.command() assert result expectedE AssertionError: assert [ps, aux] [sudo, ps, aux]E At index 0 diff: ps ! sudoE Right contains one more item: auxE Use -v to get the full difftest_exercise.py:24: AssertionError short test summary info FAILED test_exercise.py::TestAdminCommand::test_sudo - AssertionError: assert... 1 failed, 1 passed in 0.04s 输出在 test_sudo() 测试中失败。Pytest 正在提供有关正在比较的两个列表的详细信息。在这种情况下变量 result 中没有 sudo 命令这是测试预期的结果。步骤 3 - 修复 bug 并使测试通过在进行任何更改之前首先必须了解为什么会发生故障。虽然可以看到没有达到预期结果中没有 sudo但必须找出原因。满足 sudoTrue 条件时请查看 admin_command() 函数中的以下代码行if sudo:[sudo] command列表的操作不用于返回该值。由于未返回该值因此该函数最终返回不含 sudo 的命令。1.更新 admin_command() 函数以返回列表操作以便在请求 sudo 命令时使用修改的结果。更新后的函数应如下所示def admin_command(command, sudoTrue):Prefix a command with sudo unless it is explicitly not needed. Expectscommand to be a list.if sudo:return [sudo] commandreturn command2.使用 Pytest 重新运行测试。尝试在 Pytest 中使用 -v 标志来提高输出的详细程度$ pytest -v test_exercise.py3.现在请验证输出。它现在应显示两个通过的测试 test session starts Python 3.9.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0cachedir: .pytest_cacherootdir: /privatecollected 2 itemstest_exercise.py::TestAdminCommand::test_no_sudo PASSED [ 50%]test_exercise.py::TestAdminCommand::test_sudo PASSED [100%] 2 passed in 0.00s 备注由于该函数能够处理更多不同大小写的值因此应添加更多测试以涵盖这些变化。这可防止将来对函数的更改导致不同的意外行为。步骤 4 - 通过测试添加新代码在前面的步骤中添加测试后你应该能够轻松地对函数进行更多更改并用测试来验证它们。即使现有测试未涵盖这些更改你仍可以确信你不会打破任何以前的假设。在这种情况下函数 admin_command() 会盲目信任 command 参数始终是列表。让我们通过确保引发包含有用错误消息的异常来改进这一点。1.首先创建捕获行为的测试。虽然函数尚未更新但可以尝试测试优先法也称为测试驱动开发或 TDD。更新 test_exercise.py 文件使其在顶入 pytest。此测试使用 pytest 框架中的内部帮助程序import pytest现在将新的测试追加到类以检查异常。当传递给函数的值不是列表时此测试应预计函数返回 TypeErrordef test_non_list_commands(self):with pytest.raises(TypeError):admin_command(some command, sudoTrue)2.使用 Pytest 再次运行测试它们应全部通过 test session starts Python 3.9.6, pytest-6.2.5, py-1.11.0, pluggy-1.0.0rootdir: /private/collected 3 itemstest_exercise.py ... [100%] 3 passed in 0.00s 测试足以检查 TypeError但最好添加包含有用错误消息的代码。3.更新函数以显式引发包含有用错误消息的 TypeErrordef admin_command(command, sudoTrue):Prefix a command with sudo unless it is explicitly not needed. Expectscommand to be a list.if not isinstance(command, list):raise TypeError(fwas expecting command to be a list, but got a {type(command)})if sudo:return [sudo] commandreturn command4.最后更新 test_non_list_commands() 方法以检查错误消息def test_non_list_commands(self):with pytest.raises(TypeError) as error:admin_command(some command, sudoTrue)assert error.value.args[0] was expecting command to be a list, but got a class str更新的测试使用 error 作为保存所有异常信息的变量。使用 error.value.args 可以查看异常的参数。在这种情况下第一个参数包含测试可以检查的错误字符串。检查工作此时应有一个名为 test_exercise.py 的 Python 测试文件其中包含接受参数和关键字参数的 admin_command() 函数。admin_command() 函数中包含有用错误消息的 TypeError 异常。具有 command() 帮助程序方法和三个检查 admin_command() 函数的测试方法的 TestAdminCommand() 测试类。在终端中运行测试时所有测试均应通过无任何错误。04知识检查来复习一下1. 为什么对 Pytest 测试使用纯 assert 语句很有用A.它可加快测试执行速度。B.通过它可使用 Python 运算符进行任何比较。C.通过它可并行执行测试。2. 对测试类中的测试进行分组的原因之一是什么A.这样该测试可以从常用设置或清理中受益。B.这样测试便可以利用纯断言语句。C.这样该测试可以随机化以获得更好的覆盖范围。3. Pytest 可否用作用于测试的 Python 库A.否Pytest 没有其他用作库的模块和帮助程序。B.否Pytest 仅允许通过插件实现扩展性。C.是的Pytest 具有可在测试中导入的模块和帮助程序。总结在本文中我们介绍了使用 Pytest 的基础知识Pytest 是 Python 最受欢迎的测试框架之一。首先我们介绍了一些有关为 Pytest 编写测试的基础知识然后介绍了运行 Pytest 命令行工具的示例。然后你阅读了测试类和方法这些类和方法提供了与测试函数不同的灵活性。最后你尝试了为有问题的函数生成测试。通过测试可以找出问题并进行一些修复。现在你应该能够轻松查看为 Pytest 编写的测试。使用测试函数或类你应该能够为 Pytest 编写新测试或扩展现有测试套件。感谢每一个认真阅读我文章的人礼尚往来总是要有的虽然不是什么很值钱的东西如果你用得到的话可以直接拿走这些资料对于【软件测试】的朋友来说应该是最全面最完整的备战仓库这个仓库也陪伴上万个测试工程师们走过最艰难的路程希望也能帮助到你!有需要的小伙伴可以点击下方小卡片领取