如何对 C++/WinRT 组件进行单元测试?最好有代码覆盖率

How to Unit Test a C++/WinRT Component? Preferably with Code Coverage

我正在编写一些新的基于 C++/WinRT 的组件,以替换一些旧得多的 C++/CX 代码。目标是能够使用不理解 CX 的第三方 C++ 工具(静态代码分析器等)。

然而,旅程的第一步是确保我可以正确地对自己的代码进行单元测试。单元测试 C++/CX 代码通常使用 "C++ Unit Test App" 项目类型,它基于 C++/CX 并且有其自身的问题(缺乏代码覆盖支持,运行 在资源管理器中显示测试之前需要所有这些,稳定性等)

浏览 Visual Studio 2017 年的可用项目类型,我没有看到基于 C++/WinRT 的项目的单元测试项目模板。我唯一的选择是使用具有所有缺点的 "C++ Unit Test App" 模板,还是有另一种方法来为 C++/WinRT 库构建测试?

也许有一种方法可以配置 "Native Unit Test Project" 或 "Google Test" 项目模板来支持我正在寻找的内容?

理想情况下,我正在寻找的东西不需要启动 UI,是纯 C++(/WinRT),并且支持 Visual Studio 的代码覆盖率分析。

没有特定于 C++/WinRT 的单元测试项目,就像没有针对 STL 等其他库的单元测试项目一样。我会推荐 Catch2,因为它支持 C++17(C++/WinRT 的要求)并且在 Windows 上运行良好。它也是我们用于测试 C++/WinRT 本身的工具。 Catch2 很好,因为它可以帮助您创建一个简单的控制台应用程序,充当包含所有测试的测试 driver。

对于代码覆盖率,我没有强烈的建议,但如果您正在使用 Visual Studio,那么您可能想尝试 VSInstr。它可用于代码覆盖率并生成可使用 Visual Studio 查看的报告。

确保您的代码是使用 /profile 链接器选项构建的。这将确保配置文件挂钩包含在 PE 文件的专用部分中。接下来,运行 vsinstr 检测您感兴趣的任何二进制文件(之前使用 /profile 构建):

vsinstr /coverage tests.exe

现在 运行 vsperfcmd 开始收集覆盖率数据:

vsperfcmd /start:coverage /output:report

运行 代码正常。对于 Catch2,您可以简单地 运行 命令行中的可执行文件。然后你需要停止collection如下:

vsperfcmd /shutdown

大功告成。您现在可以在 Visual Studio:

中查看报告
devenv report.coverage

希望对您有所帮助。同样,这并非特定于 C++/WinRT,因为 C++/WinRT 是一个 header-only 库,您可能会收到很多与您的特定项目无关的噪音。我还没有找到处理这个问题的好方法。

扩展我对@KennyKerr 对那些感兴趣的人的回答的评论...

如果您计划按照建议使用 Catch2,那么 C++/WinRT Windows 控制台应用程序模板是一个很好的起点。几乎所有您需要做的就是调整 main() 以设置 Catch2 并开始编写您的测试用例。我唯一的抱怨是 C++/WinRT 模板不允许您通过 UI 添加 Windows 运行时组件项目引用(必须通过编辑 vcxproj 来完成)。添加 NuGet 包引用可能存在类似的问题。

如我在上面的评论中所述,市场上有一个适用于 Visual Studio 2017/2019 的 Catch2 测试适配器。请注意,它需要一个 .runsettings 文件来启用适配器并告诉它哪些项目是 Catch2 测试应用程序(通过正则表达式)。如果没有正确配置的运行设置,它将找不到您的测试。我还必须增加发现超时,否则 "forgot" 我的测试偶尔会发生。

关于代码覆盖率,当使用Visual Studio时,您可以在.runsettings 文件中将代码覆盖率配置为include/exclude 函数。有关详细信息,请参阅 Microsoft's Site。对于我自己,我在 CodeCoverage 部分添加了以下内容,到目前为止效果很好:

<Functions>
  <Include>
    <Function>.*YourNamespaceHere.*</Function>
  </Include>
  <Exclude>
    <Function>winrt.*GetRuntimeClassName</Function>
    <Function>winrt::impl.*</Function>
    <Function>winrt::(?!YourNamespaceHere).*</Function>
  </Exclude>
</Functions>

对于那些像我一样尝试测试 C++/WinRT Windows 运行时组件并且拥有未作为 WRC 接口的一部分公开的代码的人,以下是我为使其可测试所做的工作。 ..

  1. 创建 C++ 共享项项目
  2. 将您的 Windows 运行时组件 (WRC) 项目的所有代码移入共享项项目,并移出 WRC 项目。今后,只有来自共享项目的 add/remove 个文件。这样,当文件为 added/removed.
  3. 时,您不必接触 WRC 或测试项目
  4. 在您的原始 WRC 项目和测试项目中添加对此共享项项目的引用
  5. 确保您的测试项目和 WRC 项目在编译设置和 project/NuGet 引用方面配置相似
  6. 编辑测试项目并确保 RootNamespace 的配置与 WRC 项目相同(可能必须通过您喜欢的编辑器完成)。这是必需的,否则生成的 headers 将以命名空间为前缀,因此不会被共享代码找到。
  7. (代码覆盖率可选)在测试项目中,启用分析(链接器>高级>分析>是)

您现在应该可以编写测试私有代码了。至于这是否是最好的方法,我留给 reader。它对我有用,而且我正在测试的代码非常简单,我不会过分担心项目定义没有完全对齐。您的里程可能会有所不同。

我会注意到,上述内容也可用于使 "Native Unit Test Project" 与 C++/WinRT 一起工作,您只需执行额外的步骤,首先将 C++/WinRT 位集成到测试项目中。