gtest的使用和源码分析

本文基于gtest-1.6.0的源码版本,且示例源码可以在gtest-example里找到。

项目结构

gtest-1.6.0
|- include 
    |- gtest
        |- gtest.h 
        |- *
|- src 
    |- gtest-all.cc
    |- gtest-main.cc
    |- *

项目结构很简单,上面列出的是关键的文件,其余的文件用星号代替了。

整个项目的入口mainsrc/gtest-main.cc里面。 不过src/gtest-main.cc这个文件源码又及其简单,如下所示:

#include <iostream>

#include "gtest/gtest.h"

GTEST_API_ int main(int argc, char **argv) {
  std::cout << "Running main() from gtest_main.cc\n";
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

其中,可以看到头文件只包含了gtest/gtest.h,同时看gtest/gtest.h的源码可知,包含头文件只需要包含这个文件就足够了,因为在这个文件里面已经include其他头文件了。

使用示范

详细使用示范的源码在gtest-example

在此主要看TFoo.cpp文件如下:

//TFoo.cpp
#include "gtest/gtest.h"

TEST(FooTest, Case1)
{
    ASSERT_EQ(1, 1);
    ASSERT_NE(1, 2);
}

这里的TEST,ASSERT_EQ,ASSERT_NE都是gtest里面的宏。

注意到,在gtest的使用中,我们只需要写一些带有TEST宏的文件,然后编译的时候和gtest的源代码一起编译在一起即可,我们无须修改gtest_main.ccmain函数的任何内容,gtest的main函数就自动能察觉我们写的TEST。这似乎很神奇?请看下文分析。

源码分析

带着上面说的问题(main自动察觉)看了一下源码。

主要核心在于TEST这个宏,追溯TEST这个宏定义之后可以发现,这个TEST其实是一个类定义。 而且在这个类定义调用了MakeAndRegisterTestInfo这个函数(见源码gtest-1.6.0/include/gtest/internal/gtest-internal.h的1217行)。

查看MakeAndRegisterTestInfo函数的定义可知,此函数将会new出一个类的实例。 并将该类实例通过GetUnitTestImpl()->AddTestInfo添加到一个vector(test_info_list_)里面。 此vector(test_info_list_)是TestCase类里面的成员变量。 其中有一个关键的信息在于,gtest的源码里面定义了一些单例对象,这些单例是定义在函数里的static变量(也就是<<effective c++>>里面强调的static用法:local static)。

所以实际上当你在各种使用TEST宏的时候,你同时也在new出对象且将它add进那些gtest定义的单例对象里面。

所以在gtest_main.cc里面只需要遍历这些单例里面存在的Test对象来进行调用即可。所以上文所好奇的问题也就一目了然了。

总结

gtest用了有一段时间了,也是最近有空才琢磨源码看看具体的内部实现,而且带着问题看源码总是能学到不少技巧。

客服

wuyanyi09 at. foxmail.com

转载请注明出处: gtest的使用和源码分析