配置和使用 Live Unit Testing

在开发应用程序时,Live Unit Testing 将在后台自动运行任何受影响的单元测试并实时显示结果和代码覆盖率。 在你修改代码时,Live Unit Testing 可提供关于所做更改如何影响现有测试和新增代码是否会被一个或多个现有测试覆盖的反馈。 此反馈可提醒你在进行 bug 修复或添加新功能时编写单元测试。

使用 Live Unit Testing 进行测试时,它会保留测试状态数据。 由于可以使用持久化数据,因此 Live Unit Testing 的性能更优异,同时还能动态运行测试,以响应代码变化。

Live Unit Testing 仅适用于 Visual Studio 的 Enterprise edition 中面向 .NET Core 或 .NET Framework 的项目。

受支持的测试框架

Live Unit Testing 适用于下表中列出的三个常用的单元测试框架。 还显示了其适配器和框架支持的最低版本。 单元测试框架都可从 NuGet.org 获得。

测试框架 Visual Studio 适配器最低版本 Framework 最低版本
xUnit.net xunit.runner.visualstudio 版本 2.2.0-beta3-build1187 xunit 1.9.2
NUnit NUnit3TestAdapter 版本 3.5.1 NUnit 版本 3.5.0
MSTest MSTest.TestAdapter 1.1.4-预览版 MSTest.TestFramework 1.0.5-预览版

如果具有较旧的基于 MSTest 的测试项目(该项目引用 Microsoft.VisualStudio.QualityTools.UnitTestFramework),并且不想移动到较新的 MSTest NuGet 包,请升级到 Visual Studio 2019 或 Visual Studio 2017。

在某些情况下,可能需要显式还原项目引用的 NuGet 包,以便 Live Unit Testing 可正常工作。 你有两个选择:

  • 通过执行解决方案的显式生成进行还原。 在顶级 Visual Studio 菜单中依次选择“生成”>“重新生成解决方案”。
  • 还原解决方案中的包。 右键单击解决方案,并选择“还原 NuGet 包”。

配置

首次为解决方案启动 Live Unit Testing 时,使用设置向导可以配置 Live Unit Testing 应生成和运行测试的方式。

停止 Live Unit Testing 后,还可以通过转到“测试”>“Live Unit Testing”>“为解决方案配置 Live Unit Testing”来打开设置向导。

运行 Live Unit Testing 时,它会创建一个工作区,该工作区是原始存储库的副本。 然后,Live Unit Testing 会将你在 Visual Studio 中所做的任何未保存更改应用到工作区、执行生成、执行测试运行,并报告最新的代码覆盖率。

应使用向导进行的第一项配置是应从何处复制文件,以及应将其复制到何处。

显示 Live Unit Testing 配置向导第 1 页的屏幕截图。

存储库根目录

存储库根目录可指定将要复制的文件夹,以创建 Live Unit Testing 工作区。 它应是存储库的根文件夹,即它应包含所有源、二进制文件和工具。 如果解决方案文件不在存储库根目录下,则可能需要更改存储库根目录。

工作区根目录

工作区根目录指定 Live Unit Testing 保留存储库克隆的文件夹。 注意指示路径太长的异常。 默认情况下,根是在主文件夹下创建的。 但是,例如,如果通常需要在驱动器 C 下创建存储库,则可以将工作区根目录调整为类似 C:\lut\Repo 的形式。

指定排除的文件

并非所有文件都应复制到 Live Unit Testing 工作区。 应从复制中排除生成期间所生成的任何项目,以便常规生成不会干扰 Live Unit Testing 生成。 此外,常规 nuget restore 命令不应干扰 Live Unit Testing nuget restore 命令。

默认情况下,Live Unit Testing 会排除以下两种文件模式之一:

  • 对于 Git 存储库,在 gitignore 文件中指定的文件不会复制到 Live Unit Testing 工作区。
  • 对于非 Git 存储库,不会将文件夹的基本列表(如 bin/obj/)复制到 Live Unit Testing 工作区。

对于更复杂的存储库,可能需要指定自己的忽略文件。 从向导中选择“<自定义>”选项。 选择“下一步”后,将会显示 Live Unit Testing 在你完成向导后创建的自定义忽略文件的内容。 它是 lutignore 文件。

说明

某些 Git 存储库需要自定义 lutignore 文件,因为可以将文件签入 gitignore文件也会忽略的 Git 存储库中。 如果没有自定义 lutignore 文件,Live Unit Testing 将不会复制这些文件,这可能会导致生成失败。

Lutignore 文件结构

lutignore 文件使用与 gitignore 文件相同的格式。 它应包含与生成过程中生成的文件夹或文件匹配的规则,以便不会将它们复制到工作区中。 对于大多数默认项目模板,以下忽略文件已足够:

[BB]IN
[OO]BJ
# WILL NOT COPY ANY BIN AND OBJ FOLDERS TO THE LIVE UNIT TESTING WORKSPACE

如果存储库具有单个生成文件夹,则忽略文件应改为列出该文件夹:

[AA]RTIFACTS/
# WILL NOT COPY THE ARTIFACTS FOLDER TO THE LIVE UNIT TESTING WORKSPACE

如果存储库包含生成文件夹中的一些其他工具,则应在匹配模式集中排除这些工具:

[AA]RTIFACTS/
![AA]RTIFACTS/TOOLS/
# WILL NOT COPY THE ARTIFACTS FOLDER TO THE LIVE UNIT TESTING WORKSPACE
# HOWEVER IT WILL COPY THE TOOLS SUBFOLDER THAT MIGHT CONTAIN TOOLS AND UTILITIES

生成选项

向导配置页的第二部分是配置生成选项的位置:

  • 生成 PDB:为了加快生成速度,Live Unit Testing 不会在生成过程中生成 PDB。 通过这些符号文件,可以在测试失败时转到堆栈跟踪。
  • 使用多个 CPU 核心进行生成:默认情况下,Live Unit Testing 会使用多个 CPU 核心执行生成,从而改进生成时间。 如果计算机速度变慢,或无法通过使用多个处理器生成解决方案,请勿选择此选项。

测试运行选项

向导配置页的最后一部分是设置测试运行选项的位置:

  • 测试用例超时:某些测试可能需要很长时间才能运行。 如果任何测试超过特定持续时间,则设置此字段会自动中止运行。 可以自动取消测试。
  • 使用多个处理器:默认情况下,Live Unit Testing 会尝试使用多个处理器来加快运行性能。 如果计算机速度变慢,或者解决方案无法并行运行测试,请不要选择此选项。 例如,如果多个测试尝试从同一文件路径写入/读取,则可能会出现这些情况。

更多配置

通过在顶级 Visual Studio 菜单栏上选择“工具”>“选项”来配置 Live Unit Testing。 在“选项”对话框的左窗格中,选择“Live Unit Testing”。

启用 Live Unit Testing 后(请参阅启动、暂停和停止 Live Unit Testing),还可通过依次选择“测试”>“Live Unit Testing”>“选项”来打开“选项”对话框。

下图显示了对话框中可用的 Live Unit Testing 配置选项。

显示 Live Unit Testing 配置选项的屏幕截图。

可配置选项包括:

  • 是否在生成和调试解决方案时暂停 Live Unit Testing。

  • 是否在系统电量低于指定阈值时暂停 Live Unit Testing。

  • 删除所有持久化数据的功能。 当 Live Unit Testing 的行为不可预测或出现异常时(这表明持久化数据已损坏),此功能会非常有用。

  • Live Unit Testing 进程可占用的内存上限。

  • 写入 Live Unit Testing“输出”窗口的信息级别。

    选项包括无日志记录(“无”)、仅限错误消息(“错误”)、错误和信息性消息(默认值为“信息”)或所有详细信息(“详细”)。

    还可以通过向名为 VS_UTE_DIAGNOSTICS 的用户级环境变量分配值“1”在 Live Unit Testing“输出”窗口中显示详细输出。 然后重启 Visual Studio。

    要捕获文件中 Live Unit Testing 的详细 MSBuild 日志消息,请将 LiveUnitTesting_BuildLog 用户级环境变量设为该文件的名称以包含日志。

自定义 Live Unit Testing 的生成

对于更复杂的解决方案,可能需要进一步自定义生成。 例如,在测试运行期间,可能无需生成翻译文件。 要加快生成速度,可以使用 Live Unit Testing 禁用翻译文件生成。 可以通过操作项目文件来执行此操作。

添加 Live Unit Testing 替代

如果解决方案需要“常规”非检测生成不需要的自定义检测 (Live Unit Testing) 生成步骤,可向项目或 .targets 文件添加代码,以便检查 BuildingForLiveUnitTesting 属性并执行自定义生成前/后步骤。

例如,可以编写以下示例来添加仅针对 Live Unit Testing 执行的另一个目标:

<Target Name="GenerateNuGetPackages" BeforeTargets="AfterBuild" Condition="'$(BuildingForLiveUnitTesting)' == 'true'">
    <Exec Command='"$(MSBuildThisFileDirectory)..\tools\GenPac" '/>
</Target>

可以使用该 BuildingForLiveUnitTesting 属性来禁用某些不应针对测试生成而执行的任务。 例如,Live Unit Testing 可设置 <RunAnalyzers>false</RunAnalyzers> 来禁用测试分析器。

Live Unit Testing 测试依赖项

可能不会复制测试运行所需的所有文件。 Live Unit Testing 会创建一个单独的文件夹,以在其中运行测试。 借助这种安排,可以在测试运行时进行生成,但不会将生成文件夹中的所有文件复制到测试文件夹。

通常,你会出于以下两个原因之一添加测试依赖项:

  • 测试取决于源树下的文件。 例如,测试会检查 resx 文件的内容,或者可能会读取一些配置文件。
  • 测试取决于它们引用的某些库。 例如,测试可运行作为依赖项生成的可执行文件。

说明

测试依赖项必须存在于安装向导中指定为存储库根的目录中。

在这两种情况下,Live Unit Testing 默认不会复制这些文件,以在最大程度上减少为运行测试而必须复制的文件数。 如果测试运行需要这些文件,则需要使用 LiveUnitTestingTestDependency 属性显式指定它们。 例如,假设我们具有以下布局:

SRC/
  CONSOLE_UTILITY/
  TEST_PROJECT/
ARTIFACTS/
  CONSOLE_UTILITY/NET472/DEBUG/
  TEST_PROJECT/NET472/DEBUG/

默认情况下,在使用 Live Unit Testing 生成这些项目时,它仅将 Artifacts/Test_Project 复制到测试文件夹。 要将源或 console_utility 添加到测试文件夹,请将以下示例添加到 test_project.csproj

<LiveUnitTestingTestDependency Include=”$(RepoRoot)/Src/ConsoleUtility” />
<LiveUnitTestingTestDependency Include=”$(RepoRoot)/Artifacts/ConsoleUtility/net472/$(Configuration)/</LiveUnitTestingTestDependency” />

启动、暂停和停止

若要启用 Live Unit Testing,请在顶级 Visual Studio 菜单中依次选择“测试”>“Live Unit Testing”>“启动”。 启用 Live Unit Testing 后,“Live Unit Testing”菜单上的可用选项从单项的“开始”变为“暂停”和“停止”

  • 暂停:可临时挂起 Live Unit Testing。

    暂停 Live Unit Testing 后,编辑器中不会显示覆盖率可视化效果,但会保留所有已收集的数据。 若要继续运行 Live Unit Testing,请在“Live Unit Testing”菜单上选择“继续”。 Live Unit Testing 将执行必要工作,以便与其暂停时所做的全部编辑保持同步,并相应地更新字形。

  • 停止:可完全停止 Live Unit Testing。 Live Unit Testing 将丢弃所收集的全部数据。

如果在不包含单元测试项目的解决方案中启动 Live Unit Testing,则“Live Unit Testing”菜单上会显示“暂停”和“停止”选项,但 Live Unit Testing 不会启动。 “输出”窗口显示以“此解决方案没有引用受支持的测试适配器...”开头的消息

在任何时候,都可以临时暂停或完全停止 Live Unit Testing。 例如,如果正在进行重构,并且知道测试会中断一段时间,则可能需要执行这些操作。

包括和排除测试项目和测试方法

在启动 Live Unit Testing 时,将会显示“Live Unit Testing 工具”窗口,并提示你选择要通过 Live Unit Testing 测试的测试集。

显示首次启动 Live Unit Testing 时展示的工具窗口的屏幕截图。

对于单元测试运行时间很少的较小解决方案,请选择“包括所有测试”,以便 Live Unit Testing 运行所有测试。

对于包含多个测试项目的解决方案,可以通过编辑播放列表来控制哪些项目及项目中的方法会参与 Live Unit Testing。 例如,如果解决方案包含数百个测试项目,可以选择一组目标测试项目,以参与 Live Unit Testing。

通过编辑 Live Unit Testing 播放列表,可以选择应运行的 Live Unit Testing,该功能的工作方式类似于测试资源管理器中的播放列表。

可通过多种方式编辑 Live Unit Testing 播放列表:

  • Live Unit Testing 工具窗口
  • 代码编辑器窗口
  • “解决方案资源管理器”
  • 在测试代码中以编程方式

关闭或重新打开解决方案时,Live Unit Testing 会将包括/排除状态另存为用户设置并将其记住。

Live Unit Testing 工具窗口

可以使用“Live Unit Testing”选项卡的播放列表编辑器在执行中包括项目、命名空间或类,或将其从执行中排除。 在工具窗口中选择“编辑播放列表”。

可以选择或清除树视图元素以包括或排除测试。 例如,如果检查单个测试,Live Unit Testing 会在更改时运行它。 如果选择类,则会运行该类中的所有测试,并且添加到该类的所有新测试也会运行。

显示 Live Unit Testing 播放列表编辑器的屏幕截图。

代码编辑器窗口

可以使用代码编辑器窗口,包括或排除个别测试方法。 在代码编辑器窗口中右键单击测试方法的签名或正文,然后选择以下选项之一:

  • Live Unit Testing>包括 <所选方法>
  • Live Unit Testing>排除 <所选方法>
  • Live Unit Testing>排除 <所选方法> 之外的所有项

“解决方案资源管理器”

要在单元测试中选择单个项目,请在启动 Live Unit Testing 后执行以下步骤:

  1. 在“解决方案资源管理器”中右键单击解决方案并依次选择“Live Unit Testing”>“排除”以排除整个解决方案。
  2. 右键单击想要包括在测试中的每个测试项目,然后依次选择“Live Unit Testing”>“包括”。

在测试代码中以编程方式

可以应用 ExcludeFromCodeCoverageAttribute 属性,以编程方式排除方法、类或结构,将其从 Live Unit Testing 中的覆盖率报表剔除。

使用以下属性,从 Live Unit Testing 中排除个别方法:

  • xUnit[Trait("Category", "SkipWhenLiveUnitTesting")]
  • NUnit[Category("SkipWhenLiveUnitTesting")]
  • MSTest[TestCategory("SkipWhenLiveUnitTesting")]

使用以下属性,从 Live Unit Testing 中排除整个测试程序集:

  • xUnit[assembly: AssemblyTrait("Category", "SkipWhenLiveUnitTesting")]
  • NUnit[assembly: Category("SkipWhenLiveUnitTesting")]
  • MSTest[assembly: TestCategory("SkipWhenLiveUnitTesting")]

查看覆盖率可视化效果

启用 Live Unit Testing 后,它将在 Visual Studio 编辑器中更新每行代码,以显示单元测试是否覆盖正在编写的代码以及是否通过了覆盖这些代码的测试。

下图显示了测试通过和失败的代码行,以及测试未覆盖的代码行。 只有通过测试而被覆盖的行带有绿色“√”。 带有红色“x”的行由一个或多个失败的测试所覆盖。 带有蓝色“➖”的行则未由任何测试覆盖。

显示 Visual Studio 中的代码覆盖率的屏幕截图。

当你在代码编辑器中修改代码后,将立即更新 Live Unit Testing 覆盖率可视化效果。 处理编辑时,可视化效果会发生更改,以通过在通过、失败和未覆盖符号下方添加圆形计时器图像来指示数据并非最新,如下图所示。

显示 Visual Studio 中的代码覆盖率(带有计时器图标)的屏幕截图。

获取有关测试状态的信息

如果将鼠标悬停在代码窗口中的成功或失败符号上,可以看到命中该行的测试数。 若要查看各个测试的状态,请选择该符号。

显示 Visual Studio 中某个符号的测试状态的屏幕截图。

除了提供测试名称和结果之外,工具提示还支持重新运行或调试测试集。 如果选择工具提示中的一个或多个测试,还可以运行或仅调试这些测试。 借助此操作,可以调试测试,而无需退出代码窗口。

在进行调试时,除了遵循可能已设置的所有断点外,程序执行还会在调试器执行返回意外结果的 Assert 方法时暂停。

如果将鼠标悬停在工具提示中的失败测试上,测试会展开以提供有关失败的详细信息,如下图所示。 若要直接转到失败的测试,请在工具提示中双击该测试。

显示 Visual Studio 中失败的测试工具提示信息的屏幕截图。

在转到失败的测试时,Live Unit Testing 会在方法签名中直观地指出测试:

  • 通过(使用半满的烧杯和绿色“✓”表示)。
  • 失败(使用半满的烧杯和红色的“🞩”表示)。
  • 不参与 Live Unit Testing(使用半满的烧杯和蓝色“➖”表示)。

非测试方法不使用符号标识。 下图展示了所有四种类型的方法。

显示 Visual Studio 中的测试方法(带有通过或未通过符号)的屏幕截图。

诊断和更正测试失败

根据未通过测试,你可以轻松对产品代码进行调试、编辑,并继续开发应用程序。 由于 Live Unit Testing 在后台运行,因此无需在调试、编辑和继续循环期间停止和重启 Live Unit Testing。

例如,上图中显示的测试失败是因为在测试方法中错误地认为非字母字符在传递到 System.Char.IsLower 方法时会返回 true。 更正测试方法后,所有测试都应该通过。 无需暂停或停止 Live Unit Testing。

Live Unit Testing 窗口

Live Unit Testing 类似于测试资源管理器,具有可供运行和调试测试以及分析测试结果的界面 。 启用 Live Unit Testing 后,测试资源管理器中的单元测试状态将立即更新。 你不必显式运行单元测试。

当 Live Unit Testing 未启用或已停止时,Live Unit Testing 将显示上次运行测试时的单元测试状态。 重启 Live Unit Testing 后,需要更改源代码才能重新运行测试。

可以通过在顶级 Visual Studio 菜单中依次选择“测试”>“Live Unit Testing”>“启动”来启动 Live Unit Testing。 还可以通过使用“视图”>“其他窗口”>“Live Unit Testing”窗口来打开“Live Unit Testing”窗口。

你可能会注意到,在“Live Unit Testing”窗口中,一些测试呈灰显状态。例如,停止并重启 Live Unit Testing 时,Live Unit Testing 窗口中的所有测试都呈灰显状态,如下图所示

灰显测试结果指示测试不属于最新的 Live Unit Test 运行。 仅在检测到这些测试或其依赖项发生更改时才会运行它们。 如果未发生更改,将避免不必要地运行测试。 在这种情况下,灰显的测试结果虽然不是最新运行的一部分,但仍然是“最新的”。

显示测试资源管理器中的淡出测试的屏幕截图。

你可以通过更改代码来重新运行任何灰显的测试。

Live Unit Testing 自动运行、更新测试结果与通过“测试资源管理器”显式运行测试结果有所不同。 区别包括:

  • 从“测试资源管理器”窗口运行或调试测试会运行常规二进制文件。 Live Unit Testing 会运行已检测的二进制文件。
  • Live Unit Testing 不会创建新的应用程序域来运行测试。 相反,它是从默认域运行测试。 从测试资源管理器窗口运行的测试确实会创建新的应用程序域。
  • Live Unit Testing 按顺序运行每个测试程序集中的测试。 在“测试资源管理器”窗口中,可以选择并行运行多个测试

取消 Live Unit Testing 测试运行

每当进行任何代码更改时,Live Unit Testing 将持续运行测试。 如果运行正在进行并且进行了更多代码更改,Live Unit Testing 将在等待第一次运行完成时对另一个运行进行排队。

每当保存文件时,Live Unit Testing 都会取消第一次运行,并立即计划排队的运行。 此过程有助于需要很长时间才能完成首次运行的方案。

请参阅