googletest简介
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 caseEXPECT_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'torMyString(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 namePrimeTableTest2, // Test case namePrimeTableImplementations); // 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 TersePrinterTEST(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";
}
} // namespaceint main(int argc, char **argv) {InitGoogleTest(&argc, argv);bool terse_output = false;if (argc > 1 && strcmp(argv[1], "--terse_output") == 0 )terse_output = true;elseprintf("%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(); //100cout << "Returned Value: " << val << endl;//getPosition指定了调用两次,这里只调用了一次,所以运行结果显示出错return EXIT_SUCCESS;
}
提示一下,这里虽然介绍了这么多,一般不使用googlemock。因为googlemock是参照python和java的mock组件做的,而python和java的mock组件已经成熟,比如模拟类已经不需要我们手写了,有个工具能自动生成。如果要自己手写,可能需要写很长代码,实现起来比较麻烦。
googletest简介相关推荐
- 关于单元测试框架GoogleTest——参考《百度文库》、大量博客
本文章集齐百家之所长(又臭又长),读者可根据自己的需求自取: 一.关于测试 1.1 为啥要测试? 1.2 测试的分类 1.3 [单元测试-参考web[song]的博客] 1.3.1.为什么单元测试? ...
- GoogleTest使用教程
1.简介 GoogleTest是由Google开发的一个C++测试框架,支持Linux.Windows和macOS操作系统,使用Bazel或CMake构建工具. 项目主页:https://github ...
- 【gTest】gtest简介及简单使用
[gTest]gtest简介及简单使用 gtest是一个跨平台(Liunx.Mac OS X.Windows.Cygwin.Windows CE and Symbian)的C++测试框架,有googl ...
- GoogleTest单元测试学习
一.GTest简介 GoogleTest是一个单元测试的框架. 单元测试: 单元测试在函数在整个工程运行起来之前,对该函数进行测试,来判断当前函数能否达到预期的效果. 使用GoogleTest: 1. ...
- LevelDb(一):LevelDb简介
1. leveldb简介 2. hello world 2.1 leveldb源码下载和编译 2.2 hello world程序 1. leveldb简介 LevelDb是google ...
- etcd 笔记(01)— etcd 简介、特点、应用场景、常用术语、分布式 CAP 理论、分布式原理
1. etcd 简介 etcd 官网定义: A highly-available key value store for shared configuration and service discov ...
- Docker学习(一)-----Docker简介与安装
一.Docker介绍 1.1什么是docker Docker是一个开源的应用容器引擎,基于Go语言并遵从Apache2.0协议开源 Docker可以让开发者打包他们的应用以及依赖包到一个轻量级,可移植 ...
- 【Spring】框架简介
[Spring]框架简介 Spring是什么 Spring是分层的Java SE/EE应用full-stack轻量级开源框架,以IOC(Inverse Of Control:反转控制)和AOP(Asp ...
- TensorRT简介
TensorRT 介绍 引用:https://arleyzhang.github.io/articles/7f4b25ce/ 1 简介 TensorRT是一个高性能的深度学习推理(Inference) ...
最新文章
- npm start 作用
- mac 下周期调度命令或脚本
- python截取html图片大小,Python打开html文件,截取屏幕截图,裁剪并保存为图像
- Machine Learning - Andrew Ng on Coursera (Week 3)
- Executor 执行器接口笔记
- malloc和free实现的原理
- python ssh登陆模块_使用python的Paramiko模块登陆SSH
- 超像素分割算法matlab_像素不够,算法来凑。这个图像超分辨率项目帮你「拍」出高清照片...
- base32解码工具_[随波逐流]CTF编码工具 V1.0
- 常考的java数据库笔试题
- HTML5 基础入门教程
- 8位数码管动态扫描显示c语言,proteus仿真之8位数码管动态扫描显示试验
- MacCMS v10 播放器配置 无法保存
- series not exists. Legend data should be same with series name or data name. 问题VUE ECHARTS
- 基于CNN的花卉识别
- 修改热血传奇服务器地址,传奇私服如何更改上线地点
- 微信公众号之刷卡支付
- 知到网课中国饮食文化单元测试试题答案总结
- 攻防世界-MISC-练习区-12(功夫再高也怕菜刀)
- scrapy+crawlspider+增量式爬取电影
热门文章
- K8S系列:no kind “Deployment“ is registered for version apps/v1
- 麒麟 嵌入式linux操作系统_体验了一下北韩操作系统RedStar红星OS;国产linux优麒麟...
- cassandra实战--物化视图(Materialized View)
- Esper学习之十:EPL语法(六)
- Kotlin 学习笔记(四)—— 协程的基础知识,面试官的最爱了~
- 高光谱遥感--原理、技术与应用(童庆禧院士等)
- 那些牛逼互联网公司里技术团队的博客
- js实现多图上传和预览(包含表单上传、ajax上传)
- Materia切换动画
- Bidirectional A*