【问题标题】:Is it possible to perform unit testing on dll's methods without an executable during build process?是否可以在构建过程中在没有可执行文件的情况下对 dll 的方法执行单元测试?
【发布时间】:2013-05-05 18:03:14
【问题描述】:

我有 63 个 DLL,每个都有各种 C++ 方法。我想验证一些具有固定输入值的方法的输出。我想知道是否可以在 compilation 构建过程中对 DLL 本身进行单元测试。

因此,DLL 的编译 构建会在 Visual Studio 的输出窗口中提供单元测试的结果。

我知道我可以通过创建可执行文件并调用方法来验证这种情况。但是,没有可执行文件可以吗?

【问题讨论】:

  • 你可以使用RunDLL32作为可执行文件,前提是你的DLL中有一个void CALLBACK UnitTest(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);方法。

标签: c++ visual-studio unit-testing dll


【解决方案1】:

正如其他人所说 - “在编译期间”测试没有意义,所以我假设您的意思是在构建过程中进行测试,这是不同的,当然可以使用构建后步骤等。

您没有指定使用哪个版本的 Visual Studio,但如果您有 VS2012,则有一个 MSDN article 准确描述了如何执行您所描述的操作。有关完整说明,请参阅链接,我在下面附上了部分屏幕截图

【讨论】:

  • 感谢您的链接。它帮助我入门。
【解决方案2】:

逐字逐句回答您的问题,答案是“否”,因为您甚至还没有完成编译 DLL 时无法对其进行测试。此外,您需要某种可执行文件来加载该 DLL,因此您可以使用脚本语言(想到带有 ctypes 的 Python)加载它,或者创建一个可执行文件。

根据 shivakumar 的建议,从 Visual Studio 中的后编译步骤调用它可能是将结果输入输出窗口的唯一方法。我个人更喜欢从外部构建脚本运行它,但我也进行了很多交叉编译,我无法从那里的后编译步骤运行东西。这也使得在出现故障时更容易调试单元测试。

【讨论】:

    【解决方案3】:

    您必须等待编译完成,以便代码中没有编译错误。

    在构建后事件中,您可以添加批处理文件,这些文件将运行您的单元测试模块并验证编译后生成的二进制文件。

    【讨论】:

      【解决方案4】:

      你要求的东西没有意义。当您说“编译”时,这意味着非常具体的事情:在调用链接器之前调用编译器。但是 C++ 代码(和 C++ 单元测试)不能那样工作。编译器必须完成对生产代码和测试的编译,然后必须将目标文件链接到库、可执行文件或两者中。然后,测试框架必须执行调用您的生产代码的测试代码才能获得结果。这些步骤在 C++ 中都不是可选的。

      相反,您可能想问是否可以将单元测试作为构建(而不是编译)的一部分运行。答案是肯定的“是的!”

      我猜您的解决方案可能包含 63 个或更多单独的 DLL 项目。对于您要测试的每个生产 DLL,例如 Foo.DLL,我建议您添加一个新的 FooTest 项目,并将单元测试代码添加到 FooTest 项目中。在 FooTest 中,在 Foo 项目上创建一个项目依赖项,这将强制 FooTest 在构建 Foo 之后构建。在 FooTest 项目中,您将有两种代码模块:包含单元测试的类,以及 FooTest.cpp,它将容纳 FooTest.EXE 程序的 main() 入口点,调用测试框架并将结果输出到控制台。

      创建您的 FooTest.cpp,使其成为控制台程序。如果您格式化测试可执行文件的输出,使其与 Visual Studio 编译器的输出相匹配,如“filename.cpp(lineNo) : error: description of failure”,则 Visual Studio 将在您单击时自动导航到文件和行它。 CppUnit 等单元测试框架可能已经有一个“CompilerOutputter”类,它可以正确格式化输出以匹配编译器的错误。

      在您的 FooTest 项目中,您还需要将输入设置为 FooTest 链接器,以便它可以链接到您尝试测试的生产代码。在 FooTest 项目的属性中,转到 Linker/Input 选项卡并将 Foo 项目的 OBJ 文件的路径添加到 Additional Dependencies。我使用的行如下所示: $(SolutionDir)Foo\Debug\obj*.obj

      在 FooTest 项目的 Build Events 属性中,调用新的 FooTest.EXE 作为构建后步骤。然后,每次单击构建时,都会构建代码并执行单元测试。项目依赖项将确保如果您更改 Foo 代码,您将编译、链接和执行 FooTest 测试。控制台输出可确保您的测试结果在 IDE 中显示为可点击的输出。

      您可以创建 63 个单独的单元测试可执行文件,或者您可以创建一个包罗万象的单元测试可执行文件。这完全是你的选择。如果您希望更快地进行构建和链接,您可能希望拥有单独的可执行文件;即使是更多的个人配置工作,您也只需执行一次,之后您就可以保留快速构建以进行小幅更改的好处。

      现在您已经准备好进行一些严肃的编码了。

      【讨论】:

      • 嗯,我考虑链接编译的一部分,而不是单独的。与预处理源相同。我不认为这是严格用来表示你所说的。总之,这里有个毛病:如果你设置了一个依赖(FooTest依赖于Foo),VS会自动将Foo链接到FooTest中。添加目标文件应该是完全没用的,它甚至可能是有害的,因为它会链接内容两次。
      • 当您试图了解正在发生的事情时,区分是至关重要的。例如,您错过了链接器的两个不同调用从一组目标文件(Foo.DLL 和 FooTest.EXE)生成两个可执行文件这一点。您还应该知道,链接永远不会改变目标文件,它只会将它们的内容复制到产品的二进制文件中并分配地址。另一种好方法是将生产代码构建到静态库中(您不链接),然后让两个项目将 Foo.DLL 和 FooTest.exe 与库链接。
      • 好吧,那部分我确实不清楚。我希望测试应用程序像所有其他应用程序一样链接到 DLL,而不是静态链接。此外,如果我想静态链接,我会将源文件包含在 DLL 和 EXE 项目中,以避免链接使用不同编译器设置编译的内容时出现问题。此外,我不想从 EXE 中导出符号。您的特定设置的基本原理是什么?为什么不使用 DLL?
      • 链接相同的 .OBJ 有两大优势。一是您不会浪费时间编译两次;另一个是您正在测试您计划部署的代码。对于您的另一个问题,单元测试是关于在细粒度的模块接口上测试代码,而不是在大多数 DLL 公开的更高级别。您可以并且应该通过 DLL 接口测试 DLL,但这通常比单元测试更高级别。
      • 单元测试不是白盒测试。可以,但也可以是块盒测试,例如一个DLL。但是,感谢您阐明为什么要以这种方式做事,现在更有意义了。
      猜你喜欢
      • 2011-07-07
      • 1970-01-01
      • 2020-01-01
      • 2015-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-22
      • 2017-05-18
      相关资源
      最近更新 更多