googletest是由谷歌的测试技术团队开发的测试框架,使用c++实现,具有跨平台等特性。
好的测试框架
引用谷歌给出的文档,好的测试应当具备以下特征:
- 测试应该是独立的和可重复的。调试一个由于其他测试而成功或失败的测试是一件痛苦的事情。googletest通过在不同的对象上运行测试来隔离测试。当测试失败时,googletest允许您单独运行它以快速调试。
- 测试应该很好地“组织”,并反映出测试代码的结构。googletest将相关测试分组到可以共享数据和子例程的测试套件中。这种通用模式很容易识别,并使测试易于维护。当人们切换项目并开始在新的代码库上工作时,这种一致性尤其有用。
- 测试应该是“可移植的”和“可重用的”。谷歌有许多与平台无关的代码;它的测试也应该是平台中立的。googletest可以在不同的操作系统上工作,使用不同的编译器,所以googletest测试可以在多种配置下工作。
- 当测试失败时,他们应该提供尽可能多的关于问题的“信息”。谷歌测试不会在第一次测试失败时停止。相反,它只停止当前的测试并继续下一个测试。还可以设置报告非致命失败的测试,在此之后当前测试将继续进行。因此,您可以在一个运行-编辑-编译周期中检测和修复多个错误。总结一下,测试失败时尽可能多地输出错误信息。通过断言,一次测试发现或修复多个问题。
- 测试框架应该将测试编写者从日常琐事中解放出来,让他们专注于测试“内容”。Googletest自动跟踪所有定义的测试,并且不要求用户为了运行它们而枚举它们。也就是说,开发人员只需要关注测试本身,自动跟踪所有定义测试而不要枚举它们。
- 测试应该是“快速的”。使用googletest,您可以在测试之间重用共享资源,并且只需要为设置/拆除支付一次费用,而无需使测试彼此依赖。一句话,测试要高效。
再给一张图
基本操作
下载安装
github下载
git clone https://github.com/google/googletest.git
或者直接下载压缩包
wget https://github.com/google/googletest/releases/tag/release-1.11.0
编译安装
# 解压后进入目录
cd googletest
cmake CMakeLists.txt
make
sudo make install
使用方法
测试使用在编译之后,实际执行之前。
# 当不想写 main 函数的时候,可以直接引入 libgtest_main.a
g++ sample.cc -o sample -lgtest -lgtest_main -lpthread
g++ sample.cc -o sample -lgmock -lgmock_main -lpthread
# 如果自己写了main函数,就不用引入 libgtest_main.a
g++ sample.cc -o sample -lgtest -lpthread
编译解决语法问题,测试解决逻辑问题。
重要文件
googletest:
- gtest.h:googletest用来单元测试的头文件
- libgtest.a:静态测试的接口都在这个静态库中实现
- libgtest_main.a:里面提供了一个main函数以及初始化libgtest.a的代码
这里可能会有疑问,这里提前写好的main函数怎么调用到我们现写的测试案例的呢?这是因为测试案例都会通过TEST()宏加入到一个全局vector中,main()函数会调用RUN_ALL_TESTS(),这就可以把所有加入到这个vector中的测试案例都执行了。
贴一下TEST()和RUN_ALL_TESTS()的核心代码
void TestCase::AddTestInfo(TestInfo * test_info) {
test_info_list_.push_back(test_info);
test_indices_.push_back(static_cast<int>(test_indices_.size()));
}
for (int test_index = 0; test_index < total_test_case_count();test_index++) {
GetMutableTestCase(test_index)->Run(); //逐个运行测试用例,执行后面的TestBody()
}
googlemock:与googletest类似,注意googlemock依赖googletest
- gmock/gmock.h:都不解释了
- libgmock.a
- libgmock_main.a
基本概念
给一个关系图
一个项目中通常只有一个单元测试,一个单元测试中包含多个测试套件,一个测试套件中包含多个测试案例,一个测试案例中有多个断言。
断言
断言其实之前接触过:assert()。googletest要比这个功能多一些。
断言成对出现,它们测试相同的东西,但对当前函数有不同的影响。 ASSERT_* 版本在失败时产生致命失败,并中止当前函数。 EXPECT_* 版本生成非致命失败,它不会中止当前函数。通常首选EXPECT_* ,因为它们允许在测试中报告一个以上的失败。但是,如果在有问题的断言失败时继续没有意义,则应该使用 ASSERT_* 。也就是说,使用ASSERT,错误就退出;使用EXPECT,错误了没有影响,继续执行。
EXPECT_TRUE( condition );
ASSERT_TRUE( condition );
EXPECT_FALSE( condition );
ASSERT_FALSE( condition );
//二元比较
//等于
EXPECT_EQ( val1 , val2 );
ASSERT_EQ( val1 , val2 );
//不等于,注意比较空指针的时候,使用EXPECT_NE( ptr , nullptr) 而不是 EXPECT_NE( ptr , NULL)
EXPECT_NE( val1 , val2 );
ASSERT_NE( val1 , val2 );
//小于
EXPECT_LT( val1 , val2 );
ASSERT_LT( val1 , val2 );
//小于等于
EXPECT_LE( val1 , val2 );
ASSERT_LE( val1 , val2 );
//大于
EXPECT_GT( val1 , val2 );
ASSERT_GT( val1 , val2 );
//大于等于
EXPECT_GE( val1 , val2 );
ASSERT_GE( val1 , val2 );
//谓词断言,能比 EXPECT_TRUE 提供更详细的错误消息
EXPECT_PRED1( pred , val1 );
EXPECT_PRED2( pred , val1 , val2 );
EXPECT_PRED3( pred , val1 , val2 , val3 );
EXPECT_PRED4( pred , val1 , val2 , val3 , val4 );
EXPECT_PRED5( pred , val1 , val2 , val3 , val4 , val5 );
ASSERT_PRED1( pred , val1 );
ASSERT_PRED2( pred , val1 , val2 );
ASSERT_PRED3( pred , val1 , val2 , val3 );
ASSERT_PRED4( pred , val1 , val2 , val3 , val4 );
ASSERT_PRED5( pred , val1 , val2 , val3 , val4 , val5 );
举例子
//TEST()是一个测试案例,第一个参数是测试套件的名字,第二个参数是测试案例的名字
TEST(FactorialTest, Negative) {
// This test is named "Negative", and belongs to the "FactorialTest" test case
EXPECT_EQ(1, Factorial(-5));
EXPECT_EQ(1, Factorial(-1));
EXPECT_GT(Factorial(-10), 0);
}
TEST(IsPrimeTest, Negative) {
// This test belongs to the IsPrimeTest test case.
EXPECT_FALSE(IsPrime(-1));
EXPECT_FALSE(IsPrime(-2));
EXPECT_FALSE(IsPrime(INT_MIN));
}
// Returns true if m and n have no common divisors except 1.
bool MutuallyPrime(int m, int n) { ... }
...
const int a = 3;
const int b = 4;
const int c = 10;
...
EXPECT_PRED2(MutuallyPrime, a, b); // Succeeds
EXPECT_PRED2(MutuallyPrime, b, c); // Fails
所有断言宏都支持输出流,也就是当出现错误的时候,我们可以通过流输出更详细的信息;注意编码问题,经流输出的信息会自动转换为 UTF-8
EXPECT_TRUE(my_condition) << "My condition is not true";
这样会在终端打印出来,可以用来输出更详细的测试信息。
googletest
主要是
#define TEST(test_suite_name,test_name)
类的测试
类的测试逻辑要按照默认构造、传参构造、拷贝构造、具体方法的顺序来测试。这样的话,其他人来看代码的话,就算只看测试代码,也能清楚类的具体实现。也就是说,这个测试案例起到了文档的功能。
//里面的方法实现就略了
class MyString {
private:
const char* c_string_;
const MyString& operator=(const MyString& rhs);
public:
// Clones a 0-terminated C string, allocating memory using new.
static const char* CloneCString(const char* a_c_string);
//
// C'tors
// The default c'tor constructs a NULL string.
MyString() : c_string_(nullptr) {}
// Constructs a MyString by cloning a 0-terminated C string.
explicit MyString(const char* a_c_string) : c_string_(nullptr) {
Set(a_c_string);
}
// Copy c'tor
MyString(const MyString& string) : c_string_(nullptr) {
Set(string.c_string_);
}
//
// D'tor. MyString is intended to be a final class, so the d'tor
// doesn't need to be virtual.
~MyString() { delete[] c_string_; }
// Gets the 0-terminated C string this MyString object represents.
const char* c_string() const { return c_string_; }
size_t Length() const { return c_string_ == nullptr ? 0 : strlen(c_string_); }
// Sets the 0-terminated C string this MyString object represents.
void Set(const char* c_string);
};
// Tests the default c'tor.
TEST(MyString, DefaultConstructor) {
const MyString s;
// Asserts that s.c_string() returns NULL.
//
// <TechnicalDetails>
//
// If we write NULL instead of
//
// static_cast<const char *>(NULL)
//
// in this assertion, it will generate a warning on gcc 3.4. The
// reason is that EXPECT_EQ needs to know the types of its
// arguments in order to print them when it fails. Since NULL is
// #defined as 0, the compiler will use the formatter function for
// int to print it. However, gcc thinks that NULL should be used as
// a pointer, not an int, and therefore complains.
//
// The root of the problem is C++'s lack of distinction between the
// integer number 0 and the null pointer constant. Unfortunately,
// we have to live with this fact.
//
// </TechnicalDetails>
EXPECT_STREQ(nullptr, s.c_string());
EXPECT_EQ(0u, s.Length());
}
const char kHelloString[] = "Hello, world!";
// Tests the c'tor that accepts a C string.
TEST(MyString, ConstructorFromCString) {
const MyString s(kHelloString);
EXPECT_EQ(0, strcmp(s.c_string(), kHelloString));
EXPECT_EQ(sizeof(kHelloString)/sizeof(kHelloString[0]) - 1,
s.Length());
}
// Tests the copy c'tor.
TEST(MyString, CopyConstructor) {
const MyString s1(kHelloString);
const MyString s2 = s1;
EXPECT_EQ(0, strcmp(s2.c_string(), kHelloString));
}
// Tests the Set method.
TEST(MyString, Set) {
MyString s;
s.Set(kHelloString);
EXPECT_EQ(0, strcmp(s.c_string(), kHelloString));
// Set should work when the input pointer is the same as the one
// already in the MyString object.
s.Set(s.c_string());
EXPECT_EQ(0, strcmp(s.c_string(), kHelloString));
// Can we set the MyString to NULL?
s.Set(nullptr);
EXPECT_STREQ(nullptr, s.c_string());
}
test fixture测试夹具
在实际测试的过程中,类与类的依赖关系比较紧密,也可能会有多个类来工作。这时就需要包装一个测试对象,这个测试对象就叫测试夹具。
如果希望一些变量在一个测试套件的多个测试案例中都可以使用,就用测试夹具。
// 定义类型,继承自 testing::Test
class TestFixtureSmpl : public testing::Test {
protected:
void SetUp() {} // 测试夹具测试前调用的函数 -- 做初始化的工作
void TearDown() {} // 测试夹具测试后调用的函数 -- 做清理的工作
};
// 需要在 TEST_F 中书写测试用例
#define TEST_F(test_fixture,test_name)
// 如果需要复用测试夹具,只需要继承自 TestFixtureSmpl
class TestFixtureSmpl_v2 : public TestFixtureSmpl {
};
举例子
// To use a test fixture, derive a class from testing::Test.
class QueueTestSmpl3 : public testing::Test {
protected: // You should make the members protected s.t. they can be
// accessed from sub-classes.
// virtual void SetUp() will be called before each test is run. You
// should define it if you need to initialize the variables.
// Otherwise, this can be skipped.
// override表示编译期间覆盖函数
void SetUp() override {
q1_.Enqueue(1);
q2_.Enqueue(2);
q2_.Enqueue(3);
}
// virtual void TearDown() will be called after each test is run.
// You should define it if there is cleanup work to do. Otherwise,
// you don't have to provide it.
//
// virtual void TearDown() {
// }
// A helper function that some test uses.
static int Double(int n) {
return 2*n;
}
// A helper function for testing Queue::Map().
void MapTester(const Queue<int> * q) {
// Creates a new queue, where each element is twice as big as the
// corresponding one in q.
const Queue<int> * const new_q = q->Map(Double);
// Verifies that the new queue has the same size as q.
ASSERT_EQ(q->Size(), new_q->Size());
// Verifies the relationship between the elements of the two queues.
for (const QueueNode<int>*n1 = q->Head(), *n2 = new_q->Head();
n1 != nullptr; n1 = n1->next(), n2 = n2->next()) {
EXPECT_EQ(2 * n1->element(), n2->element());
}
delete new_q;
}
// Declares the variables your tests want to use.
Queue<int> q0_;
Queue<int> q1_;
Queue<int> q2_;
};
// When you have a test fixture, you define a test using TEST_F
// instead of TEST.
// Tests the default c'tor.
TEST_F(QueueTestSmpl3, DefaultConstructor) {
// You can access data in the test fixture here.
EXPECT_EQ(0u, q0_.Size());
}
// Tests Dequeue().
TEST_F(QueueTestSmpl3, Dequeue) {
int * n = q0_.Dequeue();
EXPECT_TRUE(n == nullptr);
n = q1_.Dequeue();
ASSERT_TRUE(n != nullptr);
EXPECT_EQ(1, *n);
EXPECT_EQ(0u, q1_.Size());
delete n;
n = q2_.Dequeue();
ASSERT_TRUE(n != nullptr);
EXPECT_EQ(2, *n);
EXPECT_EQ(1u, q2_.Size());
delete n;
}
// Tests the Queue::Map() function.
TEST_F(QueueTestSmpl3, Map) {
MapTester(&q0_);
MapTester(&q1_);
MapTester(&q2_);
}
类型参数化
有时候相同的接口,有多个实现,依赖测试夹具。下面是复用测试代码流程
using testing::Test;
using testing::Types;
// 先申明测试夹具
template <class T>
class TestFixtureSmpl : public testing::Test {
protected:
void SetUp() {} // 测试夹具测试前调用的函数 -- 做初始化的工作
void TearDown() {} // 测试夹具测试后调用的函数 -- 做清理的工作
};
// 枚举测试类型
typedef Types<Class1, Class2, class3> Implementations;
// #define TYPED_TEST_SUITE(CaseName,Types,__VA_ARGS__...)
// 注意 casename 一定要与测试夹具的名字一致
TYPED_TEST_SUITE(TestFixtureSmpl, Implementations);
// #define TYPED_TEST(CaseName,TestName)
// 开始测试, CaseName 要与 TYPED_TEST_SUITE 一致
TYPED_TEST(TestFixtureSmpl, TestName);
例子
template <class T>
class PrimeTableTest : public testing::Test {
protected:
// The ctor calls the factory function to create a prime table
// implemented by T.
PrimeTableTest() : table_(CreatePrimeTable<T>()) {}
~PrimeTableTest() override { delete table_; }
// Note that we test an implementation via the base interface
// instead of the actual implementation class. This is important
// for keeping the tests close to the real world scenario, where the
// implementation is invoked via the base interface. It avoids
// got-yas where the implementation class has a method that shadows
// a method with the same name (but slightly different argument
// types) in the base interface, for example.
PrimeTable* const table_;
};
using testing::Types;
// Google Test offers two ways for reusing tests for different types.
// The first is called "typed tests". You should use it if you
// already know *all* the types you are gonna exercise when you write
// the tests.
// To write a typed test case, first use
//
// TYPED_TEST_SUITE(TestCaseName, TypeList);
//
// to declare it and specify the type parameters. As with TEST_F,
// TestCaseName must match the test fixture name.
// The list of types we want to test.
typedef Types<OnTheFlyPrimeTable, PreCalculatedPrimeTable> Implementations;
TYPED_TEST_SUITE(PrimeTableTest, Implementations);
// Then use TYPED_TEST(TestCaseName, TestName) to define a typed test,
// similar to TEST_F.
TYPED_TEST(PrimeTableTest, ReturnsFalseForNonPrimes) {
// Inside the test body, you can refer to the type parameter by
// TypeParam, and refer to the fixture class by TestFixture. We
// don't need them in this example.
// Since we are in the template world, C++ requires explicitly
// writing 'this->' when referring to members of the fixture class.
// This is something you have to learn to live with.
EXPECT_FALSE(this->table_->IsPrime(-5));
EXPECT_FALSE(this->table_->IsPrime(0));
EXPECT_FALSE(this->table_->IsPrime(1));
EXPECT_FALSE(this->table_->IsPrime(4));
EXPECT_FALSE(this->table_->IsPrime(6));
EXPECT_FALSE(this->table_->IsPrime(100));
}
TYPED_TEST(PrimeTableTest, ReturnsTrueForPrimes) {
EXPECT_TRUE(this->table_->IsPrime(2));
EXPECT_TRUE(this->table_->IsPrime(3));
EXPECT_TRUE(this->table_->IsPrime(5));
EXPECT_TRUE(this->table_->IsPrime(7));
EXPECT_TRUE(this->table_->IsPrime(11));
EXPECT_TRUE(this->table_->IsPrime(131));
}
TYPED_TEST(PrimeTableTest, CanGetNextPrime) {
EXPECT_EQ(2, this->table_->GetNextPrime(0));
EXPECT_EQ(3, this->table_->GetNextPrime(2));
EXPECT_EQ(5, this->table_->GetNextPrime(3));
EXPECT_EQ(7, this->table_->GetNextPrime(5));
EXPECT_EQ(11, this->table_->GetNextPrime(7));
EXPECT_EQ(131, this->table_->GetNextPrime(128));
}
有时候你写了某个接口,期望其他人实现它,你可能想写一系列测试,确保其他人的实现满足你的测试
// 首先声明测试类型参数化(_P 是 parameterized or pattern)
// #define TYPED_TEST_SUITE_P(SuiteName)
TYPED_TEST_SUITE_P(TestFixtureSmpl);
// 书写测试, suiteName 与上面一致
// #define TYPED_TEST_P(SuiteName,TestName)
TYPED_TEST_P(TestFixtureSmpl,TestName)
// 枚举所有测试
// #define REGISTER_TYPED_TEST_SUITE_P(SuiteName,__VA_ARGS__...)
REGISTER_TYPED_TEST_SUITE_P(TestFixtureSmpl, TestName1,TestName2,...)
// 上面定义的是抽象测试类型
// 其他人实现功能后,开始测试,假如实现了 OnTheFlyPrimeTable 和PreCalculatedPrimeTable
typedef Types<OnTheFlyPrimeTable, PreCalculatedPrimeTable> PrimeTableImplementations;
// #define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix,SuiteName,Types,__VA_ARGS__...)
INSTANTIATE_TYPED_TEST_SUITE_P(instance_name, testcase, typelist...)
例子
template <class T>
class PrimeTableTest2 : public PrimeTableTest<T> {
};
// Then, declare the test case. The argument is the name of the test
// fixture, and also the name of the test case (as usual). The _P
// suffix is for "parameterized" or "pattern".
TYPED_TEST_SUITE_P(PrimeTableTest2);
// Next, use TYPED_TEST_P(TestCaseName, TestName) to define a test,
// similar to what you do with TEST_F.
TYPED_TEST_P(PrimeTableTest2, ReturnsFalseForNonPrimes) {
EXPECT_FALSE(this->table_->IsPrime(-5));
EXPECT_FALSE(this->table_->IsPrime(0));
EXPECT_FALSE(this->table_->IsPrime(1));
EXPECT_FALSE(this->table_->IsPrime(4));
EXPECT_FALSE(this->table_->IsPrime(6));
EXPECT_FALSE(this->table_->IsPrime(100));
}
TYPED_TEST_P(PrimeTableTest2, ReturnsTrueForPrimes) {
EXPECT_TRUE(this->table_->IsPrime(2));
EXPECT_TRUE(this->table_->IsPrime(3));
EXPECT_TRUE(this->table_->IsPrime(5));
EXPECT_TRUE(this->table_->IsPrime(7));
EXPECT_TRUE(this->table_->IsPrime(11));
EXPECT_TRUE(this->table_->IsPrime(131));
}
TYPED_TEST_P(PrimeTableTest2, CanGetNextPrime) {
EXPECT_EQ(2, this->table_->GetNextPrime(0));
EXPECT_EQ(3, this->table_->GetNextPrime(2));
EXPECT_EQ(5, this->table_->GetNextPrime(3));
EXPECT_EQ(7, this->table_->GetNextPrime(5));
EXPECT_EQ(11, this->table_->GetNextPrime(7));
EXPECT_EQ(131, this->table_->GetNextPrime(128));
}
// Type-parameterized tests involve one extra step: you have to
// enumerate the tests you defined:
REGISTER_TYPED_TEST_SUITE_P(
PrimeTableTest2, // The first argument is the test case name.
// The rest of the arguments are the test names.
ReturnsFalseForNonPrimes, ReturnsTrueForPrimes, CanGetNextPrime);
// At this point the test pattern is done. However, you don't have
// any real test yet as you haven't said which types you want to run
// the tests with.
// To turn the abstract test pattern into real tests, you instantiate
// it with a list of types. Usually the test pattern will be defined
// in a .h file, and anyone can #include and instantiate it. You can
// even instantiate it more than once in the same program. To tell
// different instances apart, you give each of them a name, which will
// become part of the test case name and can be used in test filters.
// The list of types we want to test. Note that it doesn't have to be
// defined at the time we write the TYPED_TEST_P()s.
typedef Types<OnTheFlyPrimeTable, PreCalculatedPrimeTable>
PrimeTableImplementations;
INSTANTIATE_TYPED_TEST_SUITE_P(OnTheFlyAndPreCalculated, // Instance name
PrimeTableTest2, // Test case name
PrimeTableImplementations); // Type list
事件
可以通过 googletest 的事件机制,在测试前后进行埋点处理
// The interface for tracing execution of tests. The methods are organized in the order the corresponding events are fired.
class TestEventListener {
public:
virtual ~TestEventListener() {}
// Fired before any test activity starts.
virtual void OnTestProgramStart(const UnitTest& unit_test) = 0; //单元测试前调用
// Fired before each iteration of tests starts.
// There may be more than one iteration if GTEST_FLAG(repeat) is set.
// iteration is the iteration index, starting from 0.
virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration) = 0;
// Fired before environment set-up for each iteration of tests starts.
virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0; //设置环境
// Fired after environment set-up for each iteration of tests ends.
virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0;
// Fired before the test suite starts.
virtual void OnTestSuiteStart(const TestSuite& /*test_suite*/) {}
// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
virtual void OnTestCaseStart(const TestCase& /*test_case*/) {}
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
// Fired before the test starts.
virtual void OnTestStart(const TestInfo& test_info) = 0;
// Fired after a failed assertion or a SUCCEED() invocation.
// If you want to throw an exception from this function to skip to the next
// TEST, it must be AssertionException defined above, or inherited from it.
virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0;
// Fired after the test ends.
virtual void OnTestEnd(const TestInfo& test_info) = 0;
// Fired after the test suite ends.
virtual void OnTestSuiteEnd(const TestSuite& /*test_suite*/) {}
// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {}
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
// Fired before environment tear-down for each iteration of tests starts.
virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0;
// Fired after environment tear-down for each iteration of tests ends.
virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0;
// Fired after each iteration of tests finishes.
virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration) = 0;
// Fired after all test activities have ended.
virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0; //单元测试结束后调用
};
注意OnTestProgramStart、OnTestProgramEnd与OnEnvironmentsSetUpStart、OnEnvironmentsSetUpEnd的区别。两者虽然都是全局的,后方是在环境初始化时调用,也就是在main函数中的InitGoogleTest函数中调用。
例子,注意需要自己写main函数
class TersePrinter : public EmptyTestEventListener {
private:
// Called before any test activity starts.
void OnTestProgramStart(const UnitTest& /* unit_test */) override {}
// Called after all test activities have ended.
void OnTestProgramEnd(const UnitTest& unit_test) override {
fprintf(stdout, "TEST %s\n", unit_test.Passed() ? "PASSED" : "FAILED");
fflush(stdout);
}
// Called before a test starts.
void OnTestStart(const TestInfo& test_info) override {
fprintf(stdout,
"*** Test %s.%s starting.\n",
test_info.test_case_name(),
test_info.name());
fflush(stdout);
}
// Called after a failed assertion or a SUCCEED() invocation.
void OnTestPartResult(const TestPartResult& test_part_result) override {
fprintf(stdout,
"%s in %s:%d\n%s\n",
test_part_result.failed() ? "*** Failure" : "Success",
test_part_result.file_name(),
test_part_result.line_number(),
test_part_result.summary());
fflush(stdout);
}
// Called after a test ends.
void OnTestEnd(const TestInfo& test_info) override {
fprintf(stdout,
"*** Test %s.%s ending.\n",
test_info.test_case_name(),
test_info.name());
fflush(stdout);
}
}; // class TersePrinter
TEST(CustomOutputTest, PrintsMessage) {
printf("Printing something from the test body...\n");
}
TEST(CustomOutputTest, Succeeds) {
SUCCEED() << "SUCCEED() has been invoked from here";
}
TEST(CustomOutputTest, Fails) {
EXPECT_EQ(1, 2)
<< "This test fails in order to demonstrate alternative failure messages";
}
} // namespace
int main(int argc, char **argv) {
InitGoogleTest(&argc, argv);
bool terse_output = false;
if (argc > 1 && strcmp(argv[1], "--terse_output") == 0 )
terse_output = true;
else
printf("%s\n", "Run this program with --terse_output to change the way "
"it prints its output.");
UnitTest& unit_test = *UnitTest::GetInstance();
// If we are given the --terse_output command line flag, suppresses the
// standard output and attaches own result printer.
if (terse_output) {
TestEventListeners& listeners = unit_test.listeners();
// Removes the default console output listener from the list so it will
// not receive events from Google Test and won't print any output. Since
// this operation transfers ownership of the listener to the caller we
// have to delete it as well.
delete listeners.Release(listeners.default_result_printer());
// Adds the custom output listener to the list. It will now receive
// events from Google Test and print the alternative output. We don't
// have to worry about deleting it since Google Test assumes ownership
// over it after adding it to the list.
listeners.Append(new TersePrinter);
}
int ret_val = RUN_ALL_TESTS();
// This is an example of using the UnitTest reflection API to inspect test
// results. Here we discount failures from the tests we expected to fail.
int unexpectedly_failed_tests = 0;
for (int i = 0; i < unit_test.total_test_suite_count(); ++i) {
const testing::TestSuite& test_suite = *unit_test.GetTestSuite(i);
for (int j = 0; j < test_suite.total_test_count(); ++j) {
const TestInfo& test_info = *test_suite.GetTestInfo(j);
// Counts failed tests that were not meant to fail (those without
// 'Fails' in the name).
if (test_info.result()->Failed() &&
strcmp(test_info.name(), "Fails") != 0) {
unexpectedly_failed_tests++;
}
}
}
// Test that were meant to fail should not affect the test program outcome.
if (unexpectedly_failed_tests == 0)
ret_val = 0;
return ret_val;
}
例子
举个例子,检测内存泄漏。
new 是c++的关键字,主要做两步:
- 调用 operator new 分配内存
- 调用构造函数在步骤 1 返回的内存地址生成类对象
我们可以通过重载 new 来修改 1 的功能。
delete 与 new 类似,只是是先调用析构函数,再释放内存,我们也可以重载delete函数。
// 重载操作符 new 和 delete,接着用类的静态成员来统计调用 new 和 delete的次数
class CLeakMem {
public:
...
void* operator new(size_t allocation_size) {
allocated_++;
return malloc(allocation_size);
}
void operator delete(void* block, size_t /* allocation_size */) {
allocated_--;
free(block);
}
private:
static int allocated_;
};
int CLeakMem::allocated_ = 0;
class LeakChecker : public EmptyTestEventListener {
private:
// Called before a test starts.
void OnTestStart(const TestInfo& /* test_info */) override {
initially_allocated_ = Water::allocated();
}
// Called after a test ends.
void OnTestEnd(const TestInfo& /* test_info */) override {
int difference = Water::allocated() - initially_allocated_;
// You can generate a failure in any event handler except OnTestPartResult. Just use an appropriate Google Test assertion to do it.
EXPECT_LE(difference, 0) << "Leaked " << difference << " unit(s) of class!";
}
int initially_allocated_;
};
googlemock
当你写一个原型或测试,往往不能完全的依赖真实对象。一个 mock 对象实现与一个真实对象相同的接口,但让你在运行时指定它时,如何使用?它应该做什么?(哪些方法将被调用?什么顺序?多少次?有什么参数?会返回什么?等)
可以模拟检查它自己和调用者之间的交互,mock 用于创建模拟类和使用它们:
- 使用一些简单的宏描述你想要模拟的接口,他们将扩展到你的 mock 类的实现
- 创建一些模拟对象,并使用直观的语法指定其期望和行为
- 练习使用模拟对象的代码。 Google Mock会在出现任何违反期望的情况时立即处理
注意googlemock 依赖 googletest;调用 InitGoogleMock 时会自动调用 InitGoogleTest。
适用场景
- 测试很慢,依赖于太多的库或使用昂贵的资源
- 测试脆弱,使用的一些资源是不可靠的(例如网络)
- 测试代码如何处理失败(例如,文件校验和错误),但不容易造成失败
- 确保模块以正确的方式与其他模块交互,但是很难观察到交互。因此你希望看到观察行动结束时的副作用
- 想模拟出复杂的依赖
使用步骤
第一步,编写模拟类
#include "gmock/gmock.h" // Brings in Google Mock.
class MockTurtle : public Turtle {
public:
...
//只是说明语法,不一定是这样调用
MOCK_METHOD0(PenUp, void());
MOCK_METHOD0(PenDown, void());
MOCK_METHOD1(Forward, void(int distance));
MOCK_METHOD1(Turn, void(int degrees));
MOCK_METHOD2(GoTo, void(int x, int y));
MOCK_CONST_METHOD0(GetX, int());
MOCK_CONST_METHOD0(GetY, int());
};
第二步,设置期望
EXPECT_CALL(mock_object, method(matchers))
.Times(cardinality)
.WillOnce(action)
.WillRepeatedly(action);
第三步,调用
mock_object obj;
t1 = obj.func1();
t2 = objfunc2();
说明一下里面的参数
matchers:期待参数
EXPECT_CALL(turtle, Forward(100));
cardinality:调用次数
// turtle::Forward 将预期调用1次
EXPECT_CALL(turtle, Forward(100)).Times(1);
// turtle::Forward 将预期调用至少1次
EXPECT_CALL(turtle, Forward(100)).Times(AtLeast(1));
action:满足期望做什么
using ::testing::Return;
...
// GetX 第一次调用返回100,第二次调用返回200,第三次返回300
EXPECT_CALL(turtle, GetX())
.Times(3)
.WillOnce(Return(100))
.WillOnce(Return(200))
.WillOnce(Return(300));
// GetX 第一次调用返回100,第二次调用返回200,第三次返回0
EXPECT_CALL(turtle, GetX())
.Times(3)
.WillOnce(Return(100))
.WillOnce(Return(200));
// GetX 将会返回4次100; WillRepeatedly 中的表达式只会计算一次
int n = 100;
EXPECT_CALL(turtle, GetX())
.Times(4)
.WillRepeatedly(Return(n++));
// #2 将会覆盖 #1;调用第三次将会报错
EXPECT_CALL(turtle, Forward(_)); // #1
EXPECT_CALL(turtle, Forward(10)) // #2
.Times(2);
// 将严格按照 PenDown, Forward, PenUp 调用顺序检查
EXPECT_CALL(turtle, PenDown());
EXPECT_CALL(turtle, Forward(100));
EXPECT_CALL(turtle, PenUp());
例子
class FooInterface {
public:
virtual ~FooInterface() {}
virtual std::string getArbitraryString() = 0;
virtual int getPosition() = 0;
};
class MockFoo : public FooInterface {
public:
MOCK_METHOD0(getArbitraryString, std::string());
MOCK_METHOD0(getPosition, int());
};
#include "stdafx.h"
using namespace seamless;
using namespace std;
using ::testing::Return;
int main(int argc, char** argv) {
::testing::InitGoogleMock(&argc, argv);
int n = 100;
string value = "Hello World!";
MockFoo mockFoo;
EXPECT_CALL(mockFoo, getArbitraryString())
.Times(1)
.WillOnce(Return(value));
string returnValue = mockFoo.getArbitraryString();
cout << "Returned Value: " << returnValue << endl;
//在这里Times(2)意思是调用两次,但是下边只调用了一次,所以会报出异常
EXPECT_CALL(mockFoo, getPosition())
.Times(2)
.WillRepeatedly(Return(n++));
int val = mockFoo.getPosition(); //100
cout << "Returned Value: " << val << endl;
//getPosition指定了调用两次,这里只调用了一次,所以运行结果显示出错
return EXIT_SUCCESS;
}
提示一下,这里虽然介绍了这么多,一般不使用googlemock。因为googlemock是参照python和java的mock组件做的,而python和java的mock组件已经成熟,比如模拟类已经不需要我们手写了,有个工具能自动生成。如果要自己手写,可能需要写很长代码,实现起来比较麻烦。