googletest简介

article/2025/9/10 16:29:51

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++的关键字,主要做两步:

  1. 调用 operator new 分配内存
  2. 调用构造函数在步骤 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组件已经成熟,比如模拟类已经不需要我们手写了,有个工具能自动生成。如果要自己手写,可能需要写很长代码,实现起来比较麻烦。


http://chatgpt.dhexx.cn/article/SuhTMGK2.shtml

相关文章

破解极验(geetest)验证码

最近在搞爬虫的时候在好几个网站都碰到了一种叫做geetest的滑动条验证码,一直没有太好的办法只能在触发这个验证码后发个报警去手动处理一下。http://www.geetest.com/exp_embed是他们官网的样例。 后来研究了下觉得要破解这个验证码有这么几个问题: 无法直接通过发送…

破解滑块验证码最新版(GEETEST 95%以上通过率)

一、滑块验证码简述 有爬虫&#xff0c;自然就有反爬虫&#xff0c;就像病毒和杀毒软件一样&#xff0c;有攻就有防&#xff0c;两者彼此推进发展。而目前最流行的反爬技术验证码&#xff0c;为了防止爬虫自动注册&#xff0c;批量生成垃圾账号&#xff0c;几乎所有网站的注册页…

极验GeeTest简单demo

概述 人机验证 3.0 解决方案(基于生物行为与人工智能) 2012 年极验将人机验证从1.0时代推动到了 2.0 时代。在 5 年时间中&#xff0c;超过千亿次数据学习与优化&#xff0c;极验利用三角防护理论和 AI 智能决策引擎&#xff0c;全面更新安全架构。2017 年&#xff0c;正式推出…

极验geetest的使用

项目中会遇到 滑块验证的需求&#xff1a; 前端vue里 1.新建/utils/gt3.js "v0.4.8 Geetest Inc.";(function (window) {"use strict";if (typeof window undefined) {throw new Error(Geetest requires browser environment);}var document window.do…

爬虫进阶教程:极验(GEETEST)验证码破解教程

摘要: 爬虫最大的敌人之一是什么&#xff1f;没错&#xff0c;验证码&#xff01;Geetest作为提供验证码服务的行家&#xff0c;市场占有率还是蛮高的。遇到Geetest提供的滑动验证码怎么破&#xff1f;授人予鱼不如授人予渔&#xff0c;接下来就为大家呈现本教程的精彩内容。 一…

【已解决】安卓手机的GeeTest文件夹是什么

网上关于安卓系统手机的GeeTest目录是什么的文章和帖子&#xff0c;绝大部分打着GeeTest的标题&#xff0c;内容都是牛头不对马嘴&#xff0c;答非所问&#xff0c;没一个能解释清楚。 我刚刚找到了正式的答复如下&#xff1a; Android 手机上的“geetest”目录与名为“极验”的…

正定二次型与半正定二次型

对于实二次型其中A是实对称的&#xff0c;下列条件等价&#xff1a; 正定的 &#xff08;1&#xff09;是正定的. &#xff08;2&#xff09;它的正惯性指数p等于n. &#xff08;3&#xff09;有可逆实矩阵C&#xff0c;使得其中 &#xff08;4&#xff09;实对称矩阵A是正…

怎么对document.write写出来的内容调整对齐方式_【求职技巧】给少数人:硅谷BAT级别的简历这么写...

我什么都不会”,“我什么都没干”,“这个项目很水”,这是我在帮别人修改简历时听到的最多的几句话。难道你真的什么都不会吗?真的什么都没干吗?真的很水吗?其实很多情况下,是这样的。 但是很水就放弃治疗了吗?不会的。放下无谓的抱怨和遗憾,好好梳理自己,认真编…

c语言八皇后问题经典算法,经典算法之八皇后问题

八皇后问题是一个古老而又著名的问题&#xff0c;是学习回溯算法的一个经典案例。今天我们就一起来探究一下吧&#xff01; 时间退回到1848年&#xff0c;国际西洋棋棋手马克斯贝瑟尔提出了这样的一个问题&#xff0c; 在88格的国际象棋上摆放八个皇后&#xff0c;使其不能互相…

从八皇后问题思考回溯法

一、八皇后问题 八皇后是经典的回溯法问题&#xff0c;题目是说将八个皇后&#xff0c;放到88的国际象棋棋盘中中&#xff0c;使得任意两个皇后都不能在同一行、同一列以及同一条对角线上。下图是一个四皇后的搜索示意图。 八皇后问题可以通过暴力法求解&#xff0c;代码也很…

八皇后问题(Python)

一.问题简介 八皇后问题&#xff1a; 如何能在 8*8 的国际象棋棋盘上放置八个皇后&#xff0c;使得任何一个皇后都无法直接吃掉其他的皇后&#xff1f;为了到达此目的&#xff0c;任两个皇后都不能处于同一条横行、纵行或斜线上。 二.几种思路和方法 1.回溯法递归思想 如图所…

八皇后问题详解(四种解法)

所有源码都在github上(https://github.com/seasonyao/eight_queen_question) 如果你去百度百科八皇后这个问题,你会发现人家也是历史上有头有脸的一个问题,最后一句“计算机发明后就有一万种方式解决这个问题”读起来也让程序猿们很快活。闲话少说,开始阐述我的思路: 最…

八皇后问题

八皇后问题 八皇后问题(英文:Eight queens)&#xff0c;是由国际西洋棋棋手马克斯贝瑟尔于1848年提出的问题&#xff0c;是回溯算法的典型案例。 问题表述为:在88格的国际象棋上摆放8个皇后&#xff0c;使其不能互相攻击&#xff0c;即任意两个皇后都不能处于同一行、同一列或…

八皇后问题(适合初学者的写法)C语言

什么是八皇后问题&#xff1a; 八皇后问题&#xff0c;是一个古老而著名的问题&#xff0c;是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯贝瑟尔于1848年提出&#xff1a;在88格的国际象棋上摆放八个皇后&#xff0c;使其不能互相攻击&#xff0c;即任意两个皇后都不能…

八皇后问题,秒懂递归回溯(有图详解|c语言)

目录 &#x1f478;&#x1f3fb;前言 &#x1f478;&#x1f3fb;题目介绍 &#x1f478;&#x1f3fb;引入&#xff1a; &#x1f478;&#x1f3fb;解决思路&#xff1a; &#x1f478;&#x1f3fb;理论存在&#xff0c;实践开始&#xff01; &#x1f478;&#x1f…

利用ngrok实现域名映射局域网ip

前言 相信很多开发者都有这样的需求&#xff0c;需要让外网访问你本地的服务器&#xff0c;方便调试本地代码&#xff0c;或者让别人体验到自己做的应用。那么这时&#xff0c;我们需要做的就是将我们本地的端口映射到一个外网的端口上&#xff0c;也就是内网穿透。常见的解决…

python调用手机摄像头,实现实时调用摄像头,需要你的电脑和手机在同一个局域网内

1、android手机上安装一款APP&#xff1a;IP摄像头&#xff0c;app的图片如上图 2.调用代码如下 import cv2cv2.namedWindow("camera", 1) # 开启ip摄像头 video "http://admin:admin10.0.0.32:8081/" # 此处后的ipv4 地址需要改为app提供的地址 cap c…

02、处于不同局域网下的Socket通信(网络部分理论知识)

目录 一、服务器 1、服务器的种类和功能 2、服务器的操作系统 3、IIs、Apache、Tomcat 4、云服务器 弹性云服务器&#xff08;Elastic Cloud Server&#xff0c;ECS&#xff09; 云服务器安全组 二、OSI七层模型与TCP/IP五层模型 三、外网、内网、公网、私网 内网穿透…

使用wireshark抓取聊天信息(局域网内的udp通信)

文章目录 1&#xff0c;实验目的2&#xff0c;实验操作3&#xff0c;总结4&#xff0c;附件 1&#xff0c;实验目的 1.分析这程序所采用的是udp还是tcp 2.在抓取包中找到窃取到的聊天信息 (英文字符和汉字可能经过了某种编码转换&#xff0c;数据包中不是明文) 3.如果是网络连…

安装黑群晖找不到局域网电脑_黑群晖洗白太复杂?我用蒲公英P5轻松实现

前言: 随着网盘时代的结束,剩下的网盘供应商又开启了垄断方式,所以越来越多的小伙伴开始自己组自己的家庭NAS网络存储服务器。比如笔者的一个好基友就是如此。其实开始笔者是想让他直接一步到位,买群晖或者铁威马的NAS,在放入硬盘就可“一劳永逸”。然而,这个小伙伴看到了…