Visual Studio Code 中的 Python 测试
Python扩展支持使用 Python 内置的unittest框架和pytest进行测试。
单元测试的一些背景知识
(如果您已经熟悉单元测试,则可以跳至演练。)
单元是要测试的特定代码段,例如函数或类。单元测试是其他代码片段,专门使用各种不同的输入(包括边界和边缘情况)来测试代码单元。unittest 和 pytest 框架都可以用来编写单元测试。
例如,假设您有一个函数来验证用户在 Web 表单中输入的帐号格式:
def validate_account_number_format(account_string):
# Return False if invalid, True if valid
# ...
单元测试只关心单元的接口——它的参数和返回值——而不关心它的实现(这就是为什么函数体中没有显示代码的原因;通常您会使用其他经过良好测试的库来帮助实现该函数)。在此示例中,该函数接受任何字符串,如果该字符串包含格式正确的帐号,则返回 true,否则返回 false。
要彻底测试此函数,您需要向其抛出所有可能的输入:有效字符串、输入错误的字符串(相差一两个字符,或包含无效字符)、太短或太长的字符串、空白字符串、空参数、包含控制字符(非文本代码)的字符串、包含HTML的字符串、包含注入攻击的字符串(例如SQL命令或JavaScript代码)等等。如果经过验证的字符串稍后用于数据库查询或显示在应用程序的 UI 中,则测试注入攻击等安全案例尤其重要。
然后,对于每个输入,您可以定义函数的预期返回值(或多个值)。在此示例中,该函数应仅对格式正确的字符串返回 true。(号码本身是否是真实账户是另一回事,可以通过数据库查询在其他地方处理。)
有了所有参数和预期返回值,您现在可以编写测试本身,这些代码是使用特定输入调用函数的代码段,然后将实际返回值与预期返回值进行比较(这种比较称为断言)):
# Import the code to be tested
import validator
# Import the test framework (this is a hypothetical module)
import test_framework
# This is a generalized example, not specific to a test framework
class Test_TestAccountValidator(test_framework.TestBaseClass):
def test_validator_valid_string():
# The exact assertion call depends on the framework as well
assert(validate_account_number_format("1234567890"), True)
# ...
def test_validator_blank_string():
# The exact assertion call depends on the framework as well
assert(validate_account_number_format(""), False)
# ...
def test_validator_sql_injection():
# The exact assertion call depends on the framework as well
assert(validate_account_number_format("drop database master"), False)
# ... tests for all other cases
代码的确切结构取决于您使用的测试框架,本文后面将提供具体示例。无论如何,正如您所看到的,每个测试都很简单:使用参数调用函数并断言预期的返回值。
所有测试的综合结果就是您的测试报告,它告诉您该功能(单元)在所有测试用例中是否按预期运行。也就是说,当一个设备通过所有测试时,您可以确信它运行正常。(测试驱动开发的实践是首先实际编写测试,然后编写代码以通过越来越多的测试,直到所有测试都通过。)
由于单元测试是小的、独立的代码片段(在单元测试中,您可以避免外部依赖性并使用模拟数据或其他模拟输入),因此它们运行起来快速且廉价。此特性意味着您可以尽早且经常运行单元测试。开发人员通常甚至在将代码提交到存储库之前就运行单元测试;门控签入系统还可以在合并提交之前运行单元测试。许多持续集成系统还在每次构建后运行单元测试。尽早运行单元测试通常意味着您可以快速捕获回归,这是先前通过所有单元测试的代码行为的意外变化。因为测试失败可以很容易地追溯到特定的代码更改,所以很容易找到并修复失败的原因,这无疑比在过程中很晚才发现问题要好!
有关单元测试的一般背景,请阅读Wikipedia 上的单元测试。有关有用的单元测试示例,您可以查看https://github.com/gwtw/py-sorting,这是一个包含不同排序算法测试的存储库。
测试演练示例
Python 测试是驻留在与被测试代码不同的文件中的 Python 类。每个测试框架指定测试和测试文件的结构和命名。编写测试并启用测试框架后,VS Code 会找到这些测试并为您提供各种命令来运行和调试它们。
对于本部分,创建一个文件夹并在 VS Code 中打开它。然后创建一个文件,命名inc_dec.py
为以下代码进行测试:
def increment(x):
return x + 1
def decrement(x):
return x - 1
使用此代码,您可以体验在 VS Code 中进行测试,如以下各节所述。
配置测试
安装 Python 扩展并在编辑器中打开 Python 文件后,VS Code 活动栏上将显示一个测试烧杯图标。烧杯图标用于测试资源管理器视图。打开测试资源管理器时,如果您没有启用测试框架,您将看到配置测试按钮。选择配置测试后,系统将提示您选择测试框架和包含测试的文件夹。如果您使用的是unittest,系统还会要求您选择用于识别测试文件的文件全局模式。
注意:文件全局模式是一种定义的字符串模式,它根据通配符匹配文件或文件夹名称,然后包含或不包含。
您可以随时使用命令面板中的Python:配置测试命令来配置测试。您还可以通过设置 或 来手动配置测试,这可以在设置编辑器中或在文件中完成,如 VS Code设置文档中所述。每个框架还具有特定的配置设置,如其文件夹和模式的测试配置设置中所述。python.testing.unittestEnabled
python.testing.pytestEnabled
settings.json
如果两个框架均启用,则 Python 扩展将仅运行pytest
.
如果启用 pytest,VS Code 会提示您安装框架包(如果当前激活的环境中尚不存在该框架包):
创建测试
每个测试框架都有自己的命名测试文件和构建测试的约定,如以下各节所述。每个案例都包含两种测试方法,其中一种出于演示目的而故意设置为失败。
单元测试中的测试
创建一个名为的文件test_unittest.py
,其中包含具有两个测试方法的测试类:
import inc_dec # The code to test
import unittest # The test framework
class Test_TestIncrementDecrement(unittest.TestCase):
def test_increment(self):
self.assertEqual(inc_dec.increment(3), 4)
# This test is designed to fail for demonstration purposes.
def test_decrement(self):
self.assertEqual(inc_dec.decrement(3), 4)
if __name__ == '__main__':
unittest.main()
pytest 中的测试
创建一个名为test_pytest.py
包含两个测试方法的文件:
import inc_dec # The code to test
def test_increment():
assert inc_dec.increment(3) == 4
# This test is designed to fail for demonstration purposes.
def test_decrement():
assert inc_dec.decrement(3) == 4
测试发现
默认情况下,一旦启用框架,Python 扩展就会尝试发现测试。您还可以使用命令面板中的测试:刷新测试命令随时触发测试发现。
python.testing.autoTestDiscoverOnSaveEnabled
默认设置为true
,这意味着每当您在工作区中添加、删除或更新任何 Python 文件时,测试发现也会自动执行。要禁用此功能,请将值设置为false
,这可以在设置编辑器中或在文件中完成settings.json
,如 VS Code设置文档中所述。您需要重新加载窗口才能使此设置生效。
测试发现应用当前框架的发现模式(可以使用测试配置设置进行自定义)。默认行为如下:
-
python.testing.unittestArgs
.py
:在顶级项目文件夹中查找名称中包含“test”的任何 Python ( ) 文件。所有测试文件必须是可导入的模块或包。您可以使用配置设置自定义文件匹配模式-p
,并使用设置自定义文件夹-t
。 -
python.testing.pytestArgs
:查找.py
名称以“test_”开头或以“_test”结尾且位于当前文件夹和所有子文件夹内任意位置的任何 Python ( ) 文件。
提示:有时无法发现放置在子文件夹中的测试,因为无法导入此类测试文件。要使它们可导入,请创建一个
__init__.py
在该文件夹中命名的空文件。
如果测试发现成功,您将看到测试资源管理器中列出的测试:
如果发现失败(例如,未安装测试框架或者测试文件中存在语法错误),您将在测试资源管理器中看到一条错误消息。您可以检查Python输出面板以查看完整的错误消息(使用“视图” > “输出”菜单命令显示“输出”面板,然后从右侧的下拉列表中选择“Python” )。
一旦 VS Code 识别出测试,它就会提供多种方法来运行这些测试,如运行测试中所述。
运行测试
您可以使用以下任一操作运行测试:
-
打开测试文件后,选择测试定义行旁边的装订线中显示的绿色运行图标,如上一节所示。此命令仅运行该一种方法。
-
从命令面板,运行以下任意命令:
- 测试:运行所有测试- 运行已发现的所有测试。
- 测试:在当前文件中运行测试- 在编辑器中打开的文件中运行所有测试。
- 测试:在光标处运行测试- 仅运行编辑器中光标下的测试方法。
-
从测试资源管理器:
-
要运行所有发现的测试,请选择测试资源管理器顶部的播放按钮:
-
要运行一组特定的测试或单个测试,请选择文件、类或测试,然后选择该项目右侧的播放按钮:
-
您还可以通过测试资源管理器运行选定的测试。为此,请在要运行的测试上按住 Ctrl 键并单击(或在 macOS 上按住 Cmd 键并单击),右键单击其中一个测试,然后选择“运行测试”。
-
测试运行后,VS Code 会直接在编辑器中将结果显示为装订线装饰。失败的测试也将在编辑器中突出显示,并带有显示测试运行错误消息和所有测试运行历史记录的预览视图。您可以按Escape 键关闭视图,也可以通过打开用户设置(首选项:命令面板中的打开设置 (UI)命令)并将测试:自动打开查看视图设置的值更改为 来禁用它。never
在测试资源管理器中,显示各个测试以及包含这些测试的任何类和文件的结果。如果该文件夹中的任何测试未通过,该文件夹将显示失败图标。
VS Code 还在Python 测试日志输出面板中显示测试结果。
并行运行测试
该包支持与 pytest 并行运行测试pytest-xdist
。要启用并行测试:
-
打开集成终端并安装
pytest-xdist
软件包。有关更多详细信息,请参阅该项目的文档页面。对于 Windows
py -3 -m pip install pytest-xdist
对于 macOS/Linux
python3 -m pip install pytest-xdist
-
接下来,在项目目录中创建一个名为的文件
pytest.ini
,并添加以下内容,指定要使用的 CPU 数量。例如,将其设置为 4 个 CPU:[pytest] addopts=-n4
或者,如果您使用的是
pyproject.toml
文件[tool.pytest.ini_options] addopts="-n 4"
-
运行您的测试,这些测试现在将并行运行。
调试测试
您有时可能需要在调试器中单步执行并分析测试,要么是因为测试本身存在需要跟踪的代码缺陷,要么是为了更好地理解正在测试的代码区域失败的原因。有关调试的更多信息或了解其在 VS Code 中的工作原理,可以阅读Python 调试配置和常规 VS Code调试文章。
例如,test_decrement
前面给出的函数失败是因为断言本身有错误。以下步骤演示了如何分析测试:
-
在函数的第一行设置断点
test_decrement
。 -
右键单击函数定义旁边的装订线装饰,然后选择“调试测试” ,或者在“测试资源管理器”中选择该测试旁边的“调试测试”图标。VS Code 启动调试器并在断点处暂停。
-
在调试控制台面板中输入,
inc_dec.decrement(3)
可以看到实际结果是2,而测试中指定的预期结果是不正确的值4。 -
停止调试器并更正错误代码:
# unittest self.assertEqual(inc_dec.decrement(3), 2) # pytest assert inc_dec.decrement(3) == 2
-
保存文件并再次运行测试以确认它们通过,并看到装订线装饰也指示通过状态。
注意:运行或调试测试不会自动保存测试文件。始终确保在运行测试之前保存对测试的更改,否则您可能会对结果感到困惑,因为它们仍然反映文件的先前版本!
您可以使用命令面板中的以下命令来调试测试:
- 测试:调试所有测试- 启动工作区中所有测试的调试器。
- 测试:调试当前文件中的测试- 为您在编辑器中打开的文件中定义的测试启动调试器。
- 测试:在光标处调试测试- 仅针对光标聚焦在编辑器上的方法启动调试器。您还可以使用测试资源管理器中的“调试测试”图标来启动所选范围内的所有测试和所有发现的测试的调试器。
您还可以通过将文件中的testing.defaultGutterClickAction
设置值更改为来更改单击装订线装饰来调试测试而不是运行的默认行为。debug
settings.json
调试器对于测试的工作方式与其他 Python 代码相同,包括断点、变量检查等。要自定义调试测试的设置,您可以在工作区文件夹中的文件"purpose": ["debug-test"]
中指定。当您运行“测试:调试所有测试”、“测试:在当前文件中调试测试”和“测试:在光标处调试测试”命令时,将使用此配置。launch.json
.vscode
例如,launch.json
文件中的以下配置禁用justMyCode
调试测试的设置:
{
"name": "Python: Debug Tests",
"type": "python",
"request": "launch",
"program": "${file}",
"purpose": ["debug-test"],
"console": "integratedTerminal",
"justMyCode": false
}
如果您有多个带有 的配置条目"purpose": ["debug-test"]
,则将使用第一个定义,因为我们目前不支持此请求类型的多个定义。
测试命令
以下是在 VS Code 中使用 Python 扩展进行测试时支持的所有命令。这些都可以通过命令面板找到:
命令名称 | 描述 |
---|---|
Python: Configure Tests | 配置要与 Python 扩展一起使用的测试框架。 |
Test: Clear All Results | 清除所有测试状态,因为 UI 会跨会话保留测试结果。 |
Test: Debug Failed Tests | 调试最近测试运行中失败的测试。 |
Test: Debug Last Run | 调试在最近的测试运行中执行的测试。 |
Test: Debug Test at Cursor | 将光标聚焦在编辑器上,调试测试方法。类似于2021.9 之前版本的Python:调试测试方法...。 |
Test: Debug Tests in Current File | 在编辑器当前关注的文件中调试测试。 |
Test: Go to Next Test Failure | 如果错误查看视图已打开,请打开并移至资源管理器中失败的下一个测试的查看视图。 |
Test: Go to Previous Test Failure | 如果错误查看视图已打开,请打开并移至资源管理器中失败的上一个测试的查看视图。 |
Test: Peek Output | 打开失败的测试方法的错误查看视图。 |
Test: Refresh Tests | 执行测试发现并更新测试资源管理器以反映任何测试更改、添加或删除。与 Python 类似:发现2021.9 之前版本的测试。 |
Test: Rerun Failed Tests | 运行最近一次测试运行中失败的测试。与 Python 类似:在 2021.9 之前的版本上运行失败的测试。 |
Test: Rerun Last Run | 调试在最近的测试运行中执行的测试。 |
Test: Run All Tests | 运行所有发现的测试。相当于Python:在2021.9之前的版本上运行所有测试。 |
Test: Run Test at Cursor | 运行测试方法,将光标聚焦在编辑器上。类似于Python:在 2021.9 之前的版本上运行测试方法...。 |
Test: Run Test in Current File | 在编辑器当前关注的文件中运行测试。相当于Python:在2021.9之前的版本上运行当前测试文件。 |
Test: Show Output | 打开包含所有测试运行详细信息的输出。与 Python 类似:在 2021.9 之前的版本上显示测试输出。 |
Testing: Focus on Test Explorer View | 打开测试资源管理器视图。与测试类似:重点关注2021.9 之前版本的 Python View。 |
Test: Stop Refreshing Tests | 取消测试发现。 |
用于 pytest 的 IntelliSense
Pylance提供 IntelliSense 功能,可以帮助您更有效地使用pytest 夹具和参数化测试。
当您为测试函数输入参数时,Pylance 将为您提供一个完成列表,其中包括来自装饰器的参数名称@pytest.mark.parametrize
,以及测试文件或conftest.py
. 还支持代码导航功能,例如转到定义和查找所有引用以及重命名符号重构。
当将鼠标悬停在固定装置引用或参数化参数引用上时,Pylance 将根据固定装置的返回值或基于传递给参数化装饰器的参数的推断类型显示推断的类型注释。
Pylance 还提供代码操作,将类型注释添加到具有固定参数的测试函数。还可以通过在用户设置中python.analysis.inlayHints.pytestParameters
设置 来启用推断夹具参数类型的嵌入提示。true
测试配置设置
使用 Python 进行测试的行为由 VS Code 提供的常规 UI 设置以及特定于 Python 和您启用的框架的设置驱动。
常规用户界面设置
影响测试功能 UI 的设置由 VS Code 本身提供,当您搜索“测试”时可以在VS Code 设置编辑器中找到。
常规 Python 设置
设置 (python.testing。) |
默认 | 描述 |
---|---|---|
autoTestDiscoverOnSaveEnabled | true |
指定保存测试文件时是否启用或禁用自动运行测试发现。更改此设置后,您可能需要重新加载窗口才能应用它。 |
cwd | 无效的 | 指定测试的可选工作目录。 |
debugPort | 3000 |
用于调试单元测试的端口号。 |
promptToConfigure | true |
指定如果发现潜在的测试,VS Code 是否提示配置测试框架。 |
单元测试配置设置
单元测试设置 (python.testing。) |
默认 | 描述 |
---|---|---|
unittestEnabled | false |
指定是否启用unittest作为测试框架。应禁用 pytest 的等效设置。 |
unittestArgs | ["-v", "-s", ".", "-p", "*test*.py"] |
传递给unittest的参数,其中由空格分隔的每个元素都是列表中的单独项目。有关默认值的说明,请参阅下文。 |
unittest 的默认参数如下:
-v
设置默认详细程度。删除此参数以获得更简单的输出。-s .
指定发现测试的起始目录。如果“test”文件夹中有测试,请将参数更改为-s test
(意味着"-s", "test"
在参数数组中)。-p *test*.py
是用于寻找测试的发现模式。在本例中,它是.py
包含单词“test”的任何文件。*_test.py
如果您以不同的方式命名测试文件,例如将“_test”附加到每个文件名,则使用类似于数组的适当参数中的模式。
要在第一次失败时停止测试运行,请将快速失败选项添加"-f"
到参数数组中。
有关完整的可用选项集,请参阅unittest命令行界面。
pytest配置设置
pytest 设置 (python.testing。) |
默认 | 描述 |
---|---|---|
pytestEnabled | false |
指定是否启用 pytest 作为测试框架。应禁用单元测试的等效设置。 |
pytestPath | "pytest" |
pytest 的路径。如果 pytest 位于当前环境之外,请使用完整路径。 |
pytestArgs | [] |
传递给 pytest 的参数,其中由空格分隔的每个元素都是列表中的单独项目。请参阅pytest 命令行选项。 |
您还可以使用pytest 配置pytest.ini
中所述的文件来配置pytest 。
注意 如果安装了 pytest-cov 覆盖模块,VS Code 在调试时不会在断点处停止,因为 pytest-cov 使用相同的技术来访问正在运行的源代码。为了防止这种行为,请在调试测试时包含
--no-cov
在内pytestArgs
,例如通过添加"env": {"PYTEST_ADDOPTS": "--no-cov"}
到调试配置中。(有关如何设置启动配置的信息,请参阅上面的调试测试。)(有关更多信息,请参阅pytest-cov 文档中的调试器和 PyCharm 。)
智能感知设置
IntelliSense 设置 (python.analysis。) |
默认 | 描述 |
---|---|---|
inlayHints.pytestParameters | 错误的 | 是否显示 pytest 夹具参数类型的嵌入提示。接受的值为true 或false 。 |