简体   繁体   English

如何使用 Google Test 测试 EXE?

[英]How to test an EXE with Google Test?

I have a C++ project in Visual Studio, and have added another project exclusively for testing.我在 Visual Studio 中有一个 C++ 项目,并添加了另一个专用于测试的项目。 Both of these projects are EXEs (console apps).这两个项目都是 EXE(控制台应用程序)。 So how do I use the first project inside the second?那么如何在第二个项目中使用第一个项目呢?

Just to clarify, the question here would be somewhat self-evident if the first project was a library that one could simply include in the second project but, being an EXE, this is where the problem lies.只是为了澄清,如果第一个项目是一个可以简单地包含在第二个项目中的库,那么这里的问题将是不言而喻的,但是作为一个 EXE,这就是问题所在。

Per your comments, you have a C++ console application ( MyApp) for which you have developed some application-specific classes that you want to unit-test with googletest in Visual Studio.根据您的评论,您有一个 C++ 控制台应用程序 ( MyApp) ,您已经为其开发了一些特定于应用程序的类,您希望在 Visual Studio 中使用 googletest 对这些类进行单元测试。 How?如何?

As you say, if you wanted to unit-test a library the way to do it would be obvious.正如你所说,如果你想对一个进行单元测试,这样做的方法是显而易见的。 You would:你会:

  • 1) Create a project to create a unit-testing application ( UnitTest ). 1) 创建一个项目来创建单元测试应用程序 ( UnitTest )。
  • 2) Configure the include-search directories so that the compiler can find the library's headers. 2) 配置包含搜索目录,以便编译器可以找到库的头文件。
  • 3) Configure the library-search directories so that the linker can find the library itself. 3) 配置 library-search 目录,以便链接器可以找到库本身。
  • 4) Add the library itself to the linker inputs. 4) 将库本身添加到链接器输入。
  • 5) Make the UnitTest project dependent on the library project, so that building UnitTest ensures MyApp is up-to-date. 5) 使UnitTest项目依赖于库项目,以便构建UnitTest确保MyApp是最新的。
  • 6) Code the UnitTest app per googletest docs. 6) 根据 googletest 文档编写UnitTest应用程序。

But since the classes you want to unit-test are specific to MyApp , you don't have any library.但是由于您要进行单元测试的类是特定于MyApp ,因此您没有任何库。

A drill-sergeant answer to that is: You don't have a library containing the classes you want to unit-test?对此的回答是:您没有包含要进行单元测试的类的库? So make one!所以做一个!

That way you use 3 projects:-这样你就可以使用 3 个项目:-

  • MyAppLib , generating library that contains all the functionality you want to unit-test. MyAppLib ,生成包含要进行单元测试的所有功能的库。
  • MyApp , generating the same executable as at present, but linking MyAppLib MyApp ,生成与当前相同的可执行文件,但链接MyAppLib
  • UnitTest , generating an executable that unit-tests MyAppLib , also linking MyAppLib UnitTest ,生成对MyAppLib单元测试的可执行文件,也链接MyAppLib

However if you don't like the drill-sergeant answer you can work around it.但是,如果您不喜欢演习中士的回答,您可以解决它。

From the usual build-system point of view (the one designed into Visual Studio), the important output of the MyApp project is the build-target - the .exe .从通常的构建系统的角度(设计到 Visual Studio 的角度来看), MyApp项目的重要输出是构建目标 - .exe The .obj files generated are just intermediate by-products.生成的.obj文件只是中间的副产品。 VS offers you no support for treating these by-products as automatic linker inputs of a dependent project, and if a dependent project was also an .exe of the same sort - as it is your case - then such automatic linkage would be impossible anyhow because the main entry point would be multiply defined. VS 不支持将这些副产品视为依赖项目的自动链接器输入,并且如果依赖项目也是同类的.exe - 就像你的情况一样 - 那么这种自动链接无论如何都是不可能的,因为主要入口点将被多重定义。

But from the unit-testing point of view it's the other way round.但从单元测试的角度来看,情况正好相反。 The .exe is of no interest, whereas (some of) the .obj files wholly or partly contain the implementations of the classes you want to unit test. .exe ,而(某些) .obj文件全部或部分包含您要进行单元测试的类的实现。 In the text-book case where class foo is defined in foo.h and implemented in foo.cpp , the object file foo.obj is needed in the linkage of UnitTest .在类foofoo.h定义并在foo.cpp实现的foo.cppUnitTest的链接中需要目标文件foo.obj

For simplicity, assume that MyApp employs just one application-specific class foo , defined in foo.h and implemented in foo.cpp .为简单起见,假设MyApp仅使用一个特定于应用程序的类foo ,在foo.h定义并在foo.cpp实现。 Then you have two options for building UnitTest .然后您有两种构建UnitTest选项。

  • a) You can add foo.cpp to the source files of UnitTest . a) 您可以将foo.cpp添加到UnitTest的源文件中。 Don't copy it of course.当然不要复制 Just Add an existing item from the source folder of MyApp .只需从MyApp的源文件夹中添加一个现有项目 Then you're done, but this course has the downside that foo.cpp is exposed to untoward editing within the UnitTest project.然后你就完成了,但是这门课程的缺点是foo.cppUnitTest项目中会受到不利的编辑。

  • b) You can treat foo.obj just like a static library required for the linkage of UnitTest and follow steps 1) - 6) above. b) 您可以将foo.obj链接UnitTest所需的静态库,并按照上面的步骤 1) - 6) 进行操作。 This means in particular at step 3) that the {Debug|Release} build of UnitTest is configured with library-search directories that include \\path\\to\\MyApp\\{Debug|Release} (either in relative or absolute form).这尤其意味着在步骤 3) 中, UnitTest的 {Debug|Release} 构建配置了包含\\path\\to\\MyApp\\{Debug|Release} (相对或绝对形式)的库搜索目录。

In reality, for option b), there's very likely more than one .obj file from MyApp that you will have to link in UnitTest , and quite likely that their number will grow with time.实际上,对于选项 b),很可能有多个来自MyApp .obj文件必须在UnitTest链接,并且它们的数量很可能会随着时间的推移而增加。 Maintaining the right linkage of UnitTest could become a chore, and you might come to the conclusion that the drill-sergeant was right after all.维护UnitTest的正确链接可能会成为一件苦差事,您可能会得出结论,毕竟演习中士是对的。

Depends.要看。 Google Test is (primarily) a Unit Testing framework (oversimplifying, testing classes). Google Test(主要)是一个单元测试框架(过度简化,测试类)。 You can absolutely use is for other types of tests, but it doesn't have "built in" functionality for other types of testing, you'll have to write it yourself.您绝对可以将 is 用于其他类型的测试,但它没有用于其他类型测试的“内置”功能,您必须自己编写。

If you are trying to system test your executable, than you can run the process.如果您尝试系统测试您的可执行文件,则可以运行该过程。 I suggest using Boost.Process if you are using a multi-platform system or already have a boost dependency.如果您使用的是多平台系统或已经有 boost 依赖,我建议使用 Boost.Process。 Else, look here: launch an exe/process with stdin stdout and stderr?否则,请看这里: 使用 stdin stdout 和 stderr 启动 exe/进程?

The "tests" you write will call the executable, and can input stdin or stdout accordingly.您编写的“测试”将调用可执行文件,并可以相应地输入标准输入或标准输出。

For example:例如:

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";
}

I was in a similar situation and I set this up in a way that effectively accomplishes the same goal of Mike Kinghan's answer as far as the compiler is concerned, but goes about it a different way from the user's perspective.我处于类似的情况,我的设置方式可以有效地实现与 Mike Kinghan 的答案相同的目标,就编译器而言,但从用户的角度来看,这是一种不同的方式。

What I did was create a custom Configuration that I called "Testing".我所做的是创建一个我称之为“测试”的自定义配置。 You create a new configuration by opening the project settings, choosing "Configuration Manager..." and selecting "New..." in the configuration selection box.您可以通过打开项目设置,选择“配置管理器...”并在配置选择框中选择“新建...”来创建新配置。

When prompted, I chose to copy the settings from the default "Debug" configuration, so that I can use the debugger with my tests just the same as if I was in the "Debug" configuration.出现提示时,我选择从默认的“调试”配置中复制设置,以便我可以在测试中使用调试器,就像我在“调试”配置中一样。

Under the new Testing configuration, I set the options for the compiler and linker to use google test as you normally would.在新的测试配置下,我将编译器和链接器的选项设置为像往常一样使用 google 测试。

The important change in the properties is that I define a preprocessor variable which I have called "TESTING".属性的重要变化是我定义了一个我称之为“TESTING”的预处理器变量。

I rewrote my "main.cpp" to look something like this:我重写了我的“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...
}

What I'm trying to indicate is that I only changed a few lines right around where main is defined, I don't have to make gross changes spread throughout the file.我想表明的是,我只在定义main地方更改了几行,我不必在整个文件中进行粗略的更改。

Now that this is all set up I simply made a new source folder for my tests, and create ".cpp" files in there.现在这一切都设置好了,我只是为我的测试创建了一个新的源文件夹,并在其中创建了“.cpp”文件。 To avoid bloating the normal executable, I wrap these files with a check for the TESTING variable, so I have something like this:为了避免普通的可执行文件膨胀,我用检查 TESTING 变量来包装这些文件,所以我有这样的东西:

tests/Test.cpp:

#ifdef TESTING

#include <gtest/gtest.h>

#include "my_class_header.h"

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

#endif

I think these files still get "hit" by the compiler under Debug and Release configurations, so having a ton of these might slow down the build, but the Debug and Release objects wont get bloated with testing code.我认为这些文件在 Debug 和 Release 配置下仍然会被编译器“命中”,因此大量这些文件可能会减慢构建速度,但 Debug 和 Release 对象不会因测试代码而变得臃肿。

The two takeaways are:两个要点是:

  • Using this method, the testing code is still organized separately from the application code, but it still resides in the same Visual Studio project, which may or may not be beneficial.使用这种方法,测试代码仍然与应用程序代码分开组织,但它仍然驻留在同一个 Visual Studio 项目中,这可能有益也可能无益。 Personally I like not having to manage/worry about a second project.我个人喜欢不必管理/担心第二个项目。
  • Like Mike Kinghan said, managing and linking .obj files yourself can become a chore, but by using this method, the default Visual Studio settings manage this for you.就像 Mike Kinghan 所说的那样,自己管理和链接.obj文件可能会成为一件苦差事,但通过使用这种方法,默认的 Visual Studio 设置会为您管理。

One downside is that effectively redundant copies of all object files will get created in the "Testing" output directory.一个缺点是所有目标文件的有效冗余副本将在“测试”输出目录中创建。 With more configuration, surely there must be a way to "share" the Debug object files, but I didn't have a reason to go that far.有了更多的配置,肯定有一种方法可以“共享”调试目标文件,但我没有理由走那么远。

This is a very simple method which may be a lot easier than refactoring your application into separate libraries and a main.这是一种非常简单的方法,可能比将您的应用程序重构为单独的库和主程序要容易得多。 I don't love using preprocessor wankery, but in this case it's fairly straightforward, not too much code bloat, and accomplishes exactly what it needs to.我不喜欢使用预处理器 wankery,但在这种情况下,它相当简单,没有太多代码膨胀,并且完全完成了它需要的功能。 You could always trigger the tests another way, without using the preprocessor.您可以随时以另一种方式触发测试,而无需使用预处理器。

If you are not very rigid about having the tests in a different project, you can write the tests in your application project.如果您对在不同项目中进行测试不是很严格,您可以在您的应用程序项目中编写测试。 Then just make the application execute the tests when receiving certain command line arguments, and execute the normal application logic otherwise, ie然后让应用程序在接收到某些命令行参数时执行测试,否则执行正常的应用程序逻辑,即

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);
}

This avoids creating an unnecessary library for the sole purpose of testing, although you can still do it if it is correct structure-wise.这避免了仅为测试目的创建不必要的库,尽管如果结构正确,您仍然可以这样做。 The downside is that the testing code goes into the executable you are going to release.缺点是测试代码会进入您要发布的可执行文件。 If you don't want that I guess you need an additional configuration which specifies a pre-processor directive to disable the tests.如果你不想要,我猜你需要一个额外的配置,它指定一个预处理器指令来禁用测试。

Debugging the tests or running them automatically at post build is easy, simply by specifying "--tests" as debug args or at post build command line respectively.调试测试或在构建后自动运行它们很容易,只需分别指定“--tests”作为调试参数或在构建后命令行中。

If you want to test a console app you can run a test that opens a console window and run the exe file of the first app.如果你想测试一个控制台应用程序,你可以运行一个测试,打开一个控制台窗口并运行第一个应用程序的 exe 文件。 Then in your googletest catch the standard output from the exe you just ran.然后在您的 googletest 中捕获您刚刚运行的 exe 的标准输出。

[For more control over the first app you might need to have the first app parse arguments sent to it, eg some flags like -x or what ever you need.] [为了更好地控制第一个应用程序,您可能需要将第一个应用程序解析参数发送给它,例如 -x 之类的标志或您需要的任何东西。]

I have prepared a github repo including Visual Studio 2015 solution in parralel of Mike's "drill-sergeant" suggestion.我准备了一个 github 存储库,其中包括与 Mike 的“drill-sergeant”建议并行的 Visual Studio 2015 解决方案。 You can use it directly without any additional requirement or dependency.您可以直接使用它,无需任何额外的要求或依赖。

https://github.com/fuatcoskun/GoogleTestVS2015 https://github.com/fuatcoskun/GoogleTestVS2015

I hope it helps...我希望它有帮助...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM