c++ 如何使用Google Test测试EXE?

pbossiut  于 2023-06-07  发布在  Go
关注(0)|答案(7)|浏览(399)

我在Visual Studio中有一个C++项目,并添加了另一个专门用于测试的项目。这两个项目都是EXE(控制台应用程序)。那么,如何在第二个项目中使用第一个项目呢?
为了澄清,如果第一个项目是一个可以简单地包含在第二个项目中的库,那么这里的问题将是不言而喻的,但是,作为一个EXE,这就是问题所在。

b4lqfgs4

b4lqfgs41#

根据您的评论,您有一个C++控制台应用程序(MyApp)),您已经为其开发了一些特定于应用程序的类,您希望在Visual Studio中使用googletest进行单元测试。怎么做?
正如你所说,如果你想对一个库进行单元测试,方法是显而易见的。您将:

  • 1)创建一个项目来创建一个单元测试应用程序(UnitTest)。
  • 2)配置include-search目录,以便编译器可以找到库的头文件。
  • 3)配置库搜索目录,以便链接器可以找到库本身。
  • 4)将库本身添加到链接器输入。
  • 5)使UnitTest项目依赖于库项目,以便构建UnitTest确保MyApp是最新的。
  • 6)根据googletest文档编写UnitTest应用程序。

但是由于您想要进行单元测试的类是特定于MyApp的,因此您没有任何库。
一个教官的回答是:* 你没有一个包含你想要单元测试的类的库?那就做一个!*
这样你就可以使用3个项目:

  • MyAppLib,生成包含所有要进行单元测试的功能的库。
  • MyApp,生成与当前相同的可执行文件,但链接MyAppLib
  • UnitTest,生成一个可执行文件,对MyAppLib进行单元测试,还链接MyAppLib

然而,如果你不喜欢教官的回答,你可以绕过它。
从通常的构建系统的Angular 来看(设计到Visual Studio中的Angular ),MyApp项目的重要输出是构建目标-.exe。生成的.obj文件只是中间副产品。VS不支持将这些副产品作为依赖项目的自动链接器输入,如果依赖项目也是相同类型的.exe-就像您的情况一样-那么这样的自动链接无论如何都是不可能的,因为主入口点将被多重定义。
但是从单元测试的Angular 来看,情况正好相反。对.exe不感兴趣,而(某些).obj文件全部或部分包含您希望进行单元测试的类的实现。在教科书的情况下,类foofoo.h中定义并在foo.cpp中实现,在UnitTest的链接中需要对象文件foo.obj
为简单起见,假设MyApp只使用一个特定于应用程序的类foo,该类在foo.h中定义并在foo.cpp中实现。然后,您有两个构建UnitTest的选项。

  • a)您可以将foo.cpp添加到UnitTest的源文件中。当然,不要“复制”它。只需从MyApp的源文件夹中添加一个已有的项目即可。然后您就完成了,但是本课程的缺点是foo.cppUnitTest项目中暴露于不适当的编辑。
  • B)您可以将foo.obj视为UnitTest链接所需的 * 静态库 *,并按照上面的步骤1)- 6)进行操作。这特别地意味着在步骤3)中,{调试|UnitTest的{Release}版本配置了包含\path\to\MyApp\{Debug|Release}(以相对或绝对形式)的库搜索目录。

实际上,对于选项B),很可能有多个来自MyApp.obj文件必须链接到UnitTest中,并且它们的数量很可能会随着时间的推移而增加。保持UnitTest的正确链接可能会成为一件苦差事,你可能会得出这样的结论:教官毕竟是对的。

1cklez4t

1cklez4t2#

取决于。Google Test(主要)是一个单元测试框架(过于简化,测试类)。你完全可以将is用于其他类型的测试,但是它没有“内置”功能用于其他类型的测试,你必须自己编写它。
如果您正在尝试对可执行文件进行系统测试,则可以运行该进程。如果您使用的是多平台系统或者已经有了boost依赖项,我建议您使用Boost.Process。否则,看这里:launch an exe/process with stdin stdout and stderr?
您编写的“测试”将调用可执行文件,并可以相应地输入stdin或stdout。
例如:

std::string path_to_exectuable = "thepath";
TEST(FooTester,CheckHelpScriptReturns0)
{
 using bp =::boost::process; 
 std::vector<std::string> args; args.push_back("--help");
 bp::context ctx; 
 ctx.stdout_behavior = bp::capture_stream(); 

 bp::child c = bp::launch(exec, args, ctx); 
 bp::status s = c.wait(); 
 ASSERT_TRUE(s.exited())<<"process didn't exit!";
 ASSERT_EQ(s.exit_status(),0)<<"Help didn't return 0";
}
kcugc4gi

kcugc4gi3#

我也遇到过类似的情况,我设置的方式有效地实现了Mike Kinhan的答案,就编译器而言,但从用户的Angular 来看,它的方式不同。
我所做的是创建一个自定义配置,我称之为“测试”。通过打开项目设置,选择“Configuration Manager...”并在配置选择框中选择“New...”,可以创建新配置。
当出现提示时,我选择从默认的“Debug”配置中复制设置,这样我就可以在测试中使用调试器,就像我在“Debug”配置中一样。
在新的Testing配置下,我设置了编译器和链接器的选项,使其像往常一样使用google test。
属性中重要的变化是我定义了一个预处理器变量,我称之为“TESTING”。
我重写了我的“main.cpp”,看起来像这样:

...
// includes
// functions
// whatever
...

#ifdef TESTING
#include <gtest/gtest.h>
#endif

int main(int argc, char **argv) {
   #ifdef TESTING
   ::testing::InitGoogleTest(&argc, argv);
   int val = RUN_ALL_TESTS();
   getchar();  // not necessary, but keeps the console open
   return val;
   #endif    

   // rest of main() as normal...
}

我想指出的是,我只修改了main定义位置附近的几行,我不必在整个文件中进行大的修改。
现在一切都设置好了,我简单地为我的测试创建了一个新的源文件夹,并在其中创建“.cpp”文件。为了避免普通可执行文件膨胀,我用TESTING变量的检查来 Package 这些文件,所以我有这样的东西:
tests/Test.cpp:

#ifdef TESTING

#include <gtest/gtest.h>

#include "my_class_header.h"

TEST(TestMyClass, test_something) {
    // perform some test on class
} 

#endif

我认为这些文件在调试和发布配置下仍然会被编译器“命中”,所以有大量的这些文件可能会减慢构建速度,但是调试和发布对象不会因为测试代码而变得臃肿。
两个要点是:

  • 使用此方法,测试代码仍然与应用程序代码分开组织,但它仍然驻留在同一Visual Studio项目中,这可能有益,也可能无益。就我个人而言,我喜欢不必管理/担心第二个项目。
  • 就像Mike Kinghan说的,自己管理和链接.obj文件可能会成为一件苦差事,但是通过使用这种方法,默认的Visual Studio设置可以为您管理这一点。

一个缺点是,所有对象文件的冗余副本将在“Testing”输出目录中创建。有了更多的配置,肯定有一种方法可以“共享”Debug对象文件,但我没有理由走那么远。
这是一个非常简单的方法,可能比将应用程序重构为单独的库和主库要容易得多。我不喜欢使用预处理器,但在这种情况下,它相当简单,没有太多的代码膨胀,并完全完成了它需要的。您总是可以用另一种方式触发测试,而不使用预处理器。

c86crjj0

c86crjj04#

如果您对在不同的项目中进行测试不是很严格,那么您可以在应用程序项目中编写测试。然后只要让应用程序在接收到某些命令行参数时执行测试,否则执行正常的应用程序逻辑,即

int main(int argc, char* argv[])
{
    if (argc >= 2 && std::string(argv[1]) == "--tests")
    {
        ::testing::InitGoogleTest(&argc, argv);
        return RUN_ALL_TESTS();
    }
    else
    {
        // Application logic goes here
    }
}

TEST(ExampleTests, TestSQRTCalculation) // assuming all the right headers are included
{
    EXPECT_NEAR(2.0, std::sqrt(4.0), 0.000001);
}

这避免了为测试的唯一目的而创建不必要的库,尽管如果结构正确,您仍然可以这样做。缺点是测试代码会进入您要发布的可执行文件。如果你不想这样,我想你需要一个额外的配置,指定一个预处理器指令来禁用测试。
调试测试或在构建后自动运行测试很容易,只需分别指定“--tests”作为调试参数或在构建后命令行中指定。

vshtjzan

vshtjzan5#

如果你想测试一个控制台应用程序,你可以运行一个测试,打开一个控制台窗口,并运行第一个应用程序的exe文件。然后在你的googletest中捕获你刚刚运行的exe的标准输出。
[For对第一个应用程序的更多控制,您可能需要让第一个应用程序解析发送给它的参数,例如一些像-x或任何你需要的标志。

bvjveswy

bvjveswy6#

我已经准备了一个github repo,包括Visual Studio 2015解决方案,与Mike的“drill-sergeant”建议并行。您可以直接使用它,而无需任何额外的要求或依赖。
https://github.com/fuatcoskun/GoogleTestVS2015
希望能帮上忙。。

svgewumm

svgewumm7#

另一种解决方案是为测试添加条件变量。然后在开发过程中将应用构建为库,当您想要发布它时,您可以禁用测试并生成可执行文件。例如,在主CMakeLists.txt中,可以添加

if(ENABLE_TESTS)
  add_subdirectory(myapp_test)
endif()

在您的应用程序中

if(ENABLE_TESTS)
  add_library(myapp)
else()
  add_executable(myapp)
endif

然后在你的测试目标中

target_link_libraries(myapp_test myapp gtest)

不要忘记对你的应用程序的主函数做同样的事情,以避免与你的测试框架中包含的主函数发生冲突

#ifdef ENABLE_TESTS
int main(int argc, char** args) {}
#endif

相关问题