简体   繁体   English

Googletest Eclipse C++:如何同时拥有测试和生产可执行文件?

[英]Googletest Eclipse C++ : How to have both test and production executable?

I have a basic question regarding Googletest in Eclipse.我有一个关于 Eclipse 中的Googletest的基本问题。

I am using the test-runner plug in to run the Googletests.我正在使用test-runner插件来运行 Googletests。 But I need to specify a binary which runs my unit tests (of course that makes sense.)但是我需要指定一个运行我的单元测试的二进制文件(当然这是有道理的。)

The problem is that in my project I now have two main functions, one to run the actual program and one问题是在我的项目中我现在有两个主要功能,一个运行实际程序,一个

int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

to run the google tests.运行谷歌测试。

Each time I want to run one I comment the other out, which of course is stupid.每次我想运行一个时,我都会将另一个注释掉,这当然是愚蠢的。

But what practice do you use to handle this situation?但是你用什么方法来处理这种情况呢?

Googletest C++ is a unit-testing framework. Googletest C++ 是一个单元测试框架。 That means it is intended for testing implementations of C++ APIs.这意味着它旨在测试 C++ API 的实现。 It isn't intended for testing programs .它不适用于测试程序

For practical purposes a C++ API is what you get in a C++ header file.出于实际目的,C++ API 是您在 C++ 头文件中获得的内容。 The implementation of such an an API might be:这种 API 的实现可能是:

  • Just the header file itself.只是头文件本身。 (The implementation is entirely inline) (实现完全是内联的)
  • The header file plus a single C++ source file头文件加上单个 C++ 源文件
  • The header file plus a bunch of C++ source files头文件加上一堆C++源文件

To generalize, the implementation of a C++ API is a header file plus 0 or more source files.概括地说,C++ API 的实现是一个头文件加上 0 个或多个源文件。

Say your program my_prog invokes an API that you or your team have developed for managing gizmos.假设您的程序my_prog调用您或您的团队为管理my_prog的 API。 The implementation is something like:实现是这样的:

gizmo.h
[gizmo_0.cpp,...gizmo_N.cpp]

where [...] means optionally ...其中[...]表示可选...

Maybe my_prog relies on other APIs for which you or your team are responsible, but we'll stick with just one.也许my_prog依赖于您或您的团队负责的其他 API,但我们将只使用一个。 my_prog uses the gizmo API by:- my_prog通过以下my_prog使用 Gizmo API:-

  • Using #include "gizmo.h" in some source files.在某些源文件中使用#include "gizmo.h"
  • Compiling the [gizmo_0.cpp,...gizmo_N.cpp] source files, if any.编译[gizmo_0.cpp,...gizmo_N.cpp]源文件,如果有的话。
  • Linking the [gizmo_0.o,...gizmo_N.o] object files, if any.链接[gizmo_0.o,...gizmo_N.o]对象文件(如果有)。

( gizmo_0.obj , etc. if you're on Windows) gizmo_0.obj等,如果你在 Windows 上)

Testing your implementation of the gizmo API with Googletest is supposed to confirm that this implementation is correct, independently of my_prog or any other program that relies on it to manage gizmos.使用 Googletest 测试您的 Gizmo API 实现应该可以确认此实现是正确的,独立于my_prog或任何其他依赖它来管理my_prog程序。 So incorporating the unit-testing of the implementation in the implementation of my_prog is misguided:-因此,将实现的单元测试合并到my_prog的实现中是错误的:-

Maybe your colleague writes another program that also needs to manage gizmos with this implementation.也许您的同事编写了另一个程序,该程序也需要使用此实现来管理 Gizmo。 Maybe you write another one.也许再写一篇。 Is whoever writes this other program supposed to repeat the process of incorporating gizmo unit-tests into it - The same ones?编写这个其他程序的人是否应该重复将 Gizmo 单元测试合并到其中的过程 - 相同的? Different ones?不同的? - and making the program conditionally compile as either a gizmo test-harness or as whatever it's supposed to be in real life? - 并使程序有条件地编译为 Gizmo 测试工具或现实生活中应有的任何东西?

And how do you know that the gizmo implementation isn't somehow entangled with functionality that's unique to my_prog , or with the implementation of some other API that my_prog uses in the same way - so that when you or somebody else tries to reuse it in another program, it breaks or behaves wrongly?你怎么知道 Gizmo 实现并没有以某种方式与my_prog独有的功能纠缠在一起,或者与my_prog以相同方式使用的其他一些 API 的实现纠缠在一起 - 这样当你或其他人试图在另一个中重用它时程序,它中断或行为错误?

No program that relies on this gizmo implementation is the place to put its unit-testing.没有依赖于这个 Gizmo 实现的程序是进行单元测试的地方。 Making my_prog conditionally compile different main functions so it can double as a unit-test harness for your gizmo library is similar to cutting a hole in the crotch of your jeans for your head to fit through.使my_prog有条件地编译不同的main函数,以便它可以my_prog Gizmo 库的单元测试工具,这类似于在牛仔裤的裤裆上切一个洞,让您的头部穿过。

The way you're supposed to unit-test the gizmo library is to write a program that is the test-harness for this library, and nothing else.你应该单元测试的小玩意儿库的方法是编写一个程序,测试线束此库,而不是其他。 This program, say gizmo_test , will use the gizmo API in just the same way as any other program would use it, but for the sole purpose of testing the gizmo library.这个程序,比如说gizmo_test ,将以与任何其他程序使用它的方式相同的方式使用 Gizmo API,但其唯一目的是测试 Gizmo 库。 All that gizmo_test will do is execute tests of the gizmo library, by invoking its API. gizmo_test要做的就是通过调用它的 API 来执行 gizmo 库的测试。

As a first approximation, the GoogleTest recipe for gizmo_test is:作为第一个近似,GoogleTest配方gizmo_test是:

Write a header file, gizmo_test.h写一个头文件, gizmo_test.h

#include "gizmo.h" in it #include "gizmo.h"在里面

#include <gtest/gtest.h> in it #include <gtest/gtest.h>在里面

Then write your Googletest test cases in it然后在其中编写您的 Googletest 测试用例

Write the following source file gizmo_test.cpp编写以下源文件gizmo_test.cpp

#include "gizmo_test.h"

int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

Create a project gizmo_test - in Eclipse or whatever development environment or build system you use - that builds the gizmo_test executable by:创建一个项目gizmo_test - 在 Eclipse 或您使用的任何开发环境或构建系统中 - 通过以下方式构建gizmo_test可执行文件:

  • Compiling the source files gizmo_test.cpp + [gizmo_0.cpp,...gizmo_N.cpp]编译源文件gizmo_test.cpp + [gizmo_0.cpp,...gizmo_N.cpp]
  • Linking the resulting object files gizmo_test.o + [gizmo_0.o,...gizmo_N.o] , plus libgtest and any other libraries on which your gizmo library depends链接生成的目标文件gizmo_test.o + [gizmo_0.o,...gizmo_N.o] ,加上libgtest和 Gizmo 库所依赖的任何其他库

You have two projects.你有两个项目。 The one that makes my_prog and the one that makes gizmo_test .制作my_prog和制作gizmo_test In your development environment or build system, make the build of my_prog depend on the build of gizmo_test , so that when you change anything that affects the gizmo library and rebuild my_prog , gizmo_test gets rebuilt first.在你的开发环境或构建系统,使构建的my_prog取决于构建gizmo_test ,所以,当你改变任何影响的小玩意儿库和重建my_proggizmo_test首先得到重建。

That's a first approximation.这是第一个近似值。 Did you notice a while back that I started talking about your gizmo library ?你有没有注意到我开始谈论你的 Gizmo That's what you've got (or should have).这就是你所拥有的(或应该拥有的)。 In C++ and programming generally, the implementation of an an API is called a library .在 C++ 和编程中,API 的实现通常称为

And maybe you also noticed some fragility, inconvenience and wastage in the recipe for gizmo_test .也许您还注意到gizmo_test配方中的一些脆弱性、不便和浪费。 You have the same set of gizmo source files [gizmo_0.cpp,...gizmo_N.cpp] in both projects.您在两个项目中都有相同的一组 Gizmo 源文件[gizmo_0.cpp,...gizmo_N.cpp] So you might edit, compile and link them differently in two projects.因此,您可以在两个项目中以不同的方式编辑、编译和链接它们。 And they'll get compiled in both projects, either differently, which is wrong , or identically, which is pointless.它们将在两个项目中编译,要么不同,这是错误的,要么相同,这是毫无意义的。

Of course if this set of source files is empty - the gizmo library is nothing but gizmo.h - there's no such problem.当然,如果这组源文件是空的gizmo.h库就是gizmo.h就不会有这样的问题。 But if it's not empty, there is.但如果它不是空的,那就是。

As you know, in C++ we don't use a library by building its source files in every program that uses it - not unless it's a header-only library.如您所知,在 C++ 中,我们不会通过在每个使用它的程序中构建其源文件来使用库 - 除非它是仅头文件的库。 A library is built by itself into an object library (either static or dynamic), and to use it a program just includes the library's header file(s) and links the object library.库由自身构建到目标库(静态或动态)中,并且要使用它,程序只需包含库的头文件并链接目标库。

That's how a program should use your gizmo library too.这也是程序应该如何使用您的 Gizmo 库的方式。 So to the final approximation:-所以最后的近似值:-

  • Make a project libgizmo that builds a gizmo object library (static or dynamic, as you see fit).创建一个项目libgizmo来构建 Gizmo 对象库(静态或动态,如您所见)。
  • Make a project gizmo_test as above, except that instead of compiling and linking [gizmo_0.cpp,...gizmo_N.cpp] , it just links libgizmo , and make this project depend on the libgizmo project.像上面一样创建一个项目gizmo_test ,除了不是编译和链接[gizmo_0.cpp,...gizmo_N.cpp] ,它只是链接libgizmo ,并使这个项目依赖于libgizmo项目。
  • Make a project my_prog as you have it now, but instead of compiling and linking [gizmo_0.cpp,...gizmo_N.cpp] , just link libgizmo , and make this project depend on the gizmo_test project.像现在一样创建一个项目my_prog ,而不是编译和链接[gizmo_0.cpp,...gizmo_N.cpp] ,只需链接libgizmo ,并使该项目依赖于gizmo_test项目。

So you have three projects by the time you build the first program that uses the gizmo library.因此,当您构建第一个使用 Gizmo 库的程序时,您已经拥有三个项目。 Each subsequent program that uses the gizmo library needs one more project, like the my_prog project.每个使用 Gizmo 库的后续程序都需要一个更多的项目,例如my_prog项目。

Googletest is designed for testing C++ libraries , and this is how you're supposed to use it. Googletest 是为测试 C++而设计的,这就是您应该使用它的方式。

Now I know nothing about your program or how you are currently deploying Googletest test cases in your project.现在我对您的程序一无所知,也不知道您目前如何在项目中部署 Googletest 测试用例。 Maybe there aren't any well-defined API implementations in it that those test cases are supposed to exercise, that you can factor out into free-standing libraries.也许其中没有任何明确定义的 API 实现供这些测试用例使用,您可以将其分解为独立库。 Possibly that could be because your program is so very simple that unit-testing its "components" is non-applicable, and you'd be wiser just to write blackbox tests of the program.可能是因为您的程序非常简单,以至于对其“组件”进行单元测试是不适用的,您最好编写程序的黑盒测试。 More likely it would be because you've so far failed to design a program architecture that is capable of being unit-tested.更有可能是因为您迄今为止未能设计出能够进行单元测试的程序架构。 If that's what you find, you need to fix it , and then apply Googletest the right way.如果这就是你发现的,你需要修复它,然后以正确的方式应用 Googletest。 It will be worth the effort.这将是值得的。

And in case it needs pointed out, unit-tests are not program tests, so as well as unit-testing any libraries your program relies on, if they are your responsibility, you also need blackbox tests of your program.如果需要指出,单元测试不是程序测试,因此除了对程序所依赖的任何库进行单元测试之外,如果它们是您的责任,您需要对程序进行黑盒测试。

Indeed a valid question.确实是一个有效的问题。 I believe that Mike Kinghan's detailed response explains how you should use google-test but nothing is set in stone and in the end, you can use google-test in any way you want.我相信 Mike Kinghan 的详细回复解释了您应该如何使用 google-test 但没有什么是一成不变的,最后,您可以以任何方式使用 google-test。

So, since I actually encountered a similar situation, what I did was rather simple.所以,既然我也遇到过类似的情况,我做的就比较简单了。

I modified the production file source code so that the main function will change from this:我修改了生产文件源代码,以便主函数从这里改变:

void main(void)

to this:对此:

#ifndef TDD_ENABLED // TDD_ENABLED is defined only in the unit test project
    void main(void)
#else
    void main_app(void)
#endif

this way when you are running unit tests, the original 'main' function is converted to 'main_app' and you avoid conflicts with the 'main' of google test.这样,当您运行单元测试时,原始的“main”函数会转换为“main_app”,从而避免与 google test 的“main”发生冲突。 When you are running the original production code project, the 'main_app' function is named again 'main' as it should.当您运行原始生产代码项目时,'main_app' 函数应再次命名为 'main'。 So, technically, from your production code perspective, nothing has changed.因此,从技术上讲,从您的生产代码的角度来看,没有任何变化。

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

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