set(CMAKE_CXX_STANDARD 14)
后一切正常。然后安装
sudo make install
使用cmake进行构建
make install
后静态链接库声称在/usr/local/lib
目录下,我们可以使用绝对目录来连接,当然更方便的是使用cmake
来构建GTest
项目,只需要在你的CMakeLists.txt
中加入如下内容
if (UNIX)
find_package(Threads REQUIRED)
find_package(GTest REQUIRED)
if (GTest_FOUND)
include_directories(${GTEST_INCLUDE_DIR})
endif ()
else ()
endif ()
add_executable(${PROJECT_NAME} ${SRC_DIR})
target_link_libraries(${PROJECT_NAME} ${GTEST_LIBRARY})
target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT})
下面我们来看一个使用GTest
的例子。
Demo1
├── build.sh
├── CMakeLists.txt
├── gtest
├── include
│ └── Configure.h
├── main.cpp
└── src
├── Configure.cpp
└── ConfigureTest.cpp
Configure.h
#ifndef GTEST_CONFIGURE_H
#define GTEST_CONFIGURE_H
#include <string>
#include <vector>
class Configure
private:
std::vector<std::string> vItems;
public:
int addItem(std::string str);
std::string getItem(int index);
int getSize();
#endif //GTEST_CONFIGURE_H
Configure.cpp
#include <algorithm>
#include "Configure.h"
* @brief Add an item to configuration store. Duplicate item will be ignored
* @param str item to be stored
* @return the index of added configuration item
int Configure::addItem(std::string str) {
std::vector<std::string>::const_iterator vi = std::find(vItems.begin(), vItems.end(), str);
if (vi != vItems.end())
return vi - vItems.begin();
vItems.push_back(str);
return vItems.size() - 1;
* @brief Return the configure item at specified index.
* If the index is out of range, "" will be returned
* @param index the index of item
* @return the item at specified index
std::string Configure::getItem(int index) {
if (index >= vItems.size())
return "";
return vItems.at(index);
/// Retrieve the information about how many configuration items we have had
int Configure::getSize() {
return vItems.size();
ConfigureTest.cpp
#include <gtest/gtest.h>
#include "Configure.h"
TEST(ConfigureTest, addItem)
// do some initialization
auto* pc = new Configure();
// validate the pointer is not null
ASSERT_TRUE(pc != nullptr);
// call the method we want to test
pc->addItem("A");
pc->addItem("B");
pc->addItem("A");
// validate the result after operation
EXPECT_EQ(pc->getSize(), 2);
EXPECT_STREQ(pc->getItem(0).c_str(), "A");
EXPECT_STREQ(pc->getItem(1).c_str(), "B");
EXPECT_STREQ(pc->getItem(10).c_str(), "");
delete pc;
main.cpp
#include <gtest/gtest.h>
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
// Runs all tests using Google Test.
return RUN_ALL_TESTS();
编译运行:
cmake .
make -j
./gtest
Output:
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from ConfigureTest
[ RUN ] ConfigureTest.addItem
[ OK ] ConfigureTest.addItem (0 ms)
[----------] 1 test from ConfigureTest (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[ PASSED ] 1 test.
使用TEST宏创建测试用例
在Demo1的ConfigureTest.cpp文件中,使用了TEST()
这个宏来创建一个Test Case,关于TEST
宏,官方文档上主要有以下几点说明:
使用TEST
来定义或者明明一个测试函数,该函数没有返回值。
TEST
函数和任何其他C++函数相比,只多了一点:你可以使用GTest
提供的断言宏来对测试结果进行判断。
测试结果由断言确定;如果测试中的任何断言失败(致命或非致命),或者测试崩溃,则整个测试失败。
这里我们再举一个简单的例子进行说明,有一个函数其声明为:
int Factorial(int n); // Returns the factorial of n
针对这个函数的一个测试用例应该是这样的:
// Tests factorial of 0.
TEST(FactorialTest, HandlesZeroInput) {
EXPECT_EQ(Factorial(0), 1);
// Tests factorial of positive numbers.
TEST(FactorialTest, HandlesPositiveInput) {
EXPECT_EQ(Factorial(1), 1);
EXPECT_EQ(Factorial(2), 2);
EXPECT_EQ(Factorial(3), 6);
EXPECT_EQ(Factorial(8), 40320);
GTest中的断言宏
上面我们讲了,断言宏是使用在TEST
测试用例中,用来判断测试执行结果的方法。
GTest
的断言宏有两种:1. 形如ASSERT_*
2. 形如EXPECT_*
。第一种类似assert.h
头文件中的assert
方法,如果表达之为false,则直接调用abort
使程序退出;而第二种即使表达式为false,也不会退出程序,只会相应日志,继续执行其他测试用例。官方推荐使用第二种。
至于*部分,主要包括一下几种
创建一个继承自::testing::Test
的类,其所有成员均为protected类型,因为他的成员函数/变量会在子类中被访问。
把你想要在多个测试用例中共享的数据在构造函数或者SetUp()
方法中初始化。
如你在SetUp()
中使用了动态内存,那么必须在声明一个TearDown()
方法里回收这些内存。
使用TEST_F(TestFixtureName, TestName)
而不是TEST
。
TEST_F()
的第一个参数应该是你声明的fixture
类的类名。
由于C++中宏定义不允许使用单一的宏来处理所有的测试类型,所以你必须在TEST_F()
之前定义一个fixture
类,不然会报编译错误virtual outside class declaration.
同一个测试用例里的不同的测试具有独立的fxiture
对象,GTest
会在下一次测试开始前删除之前的fixture
对象,并创建一个新的。所以,当前测试对fxiture
的修改,不会影响下一次测试。
下面是一个FIFO Queue
的例子:
template <typename E> // E is the element type.
class Queue {
public:
Queue();
void Enqueue(const E& element);
E* Dequeue(); // Returns NULL if the queue is empty.
size_t size() const;
//First, define a fixture class. By convention, you should give it the name FooTest where Foo is the class being tested.
class QueueTest : public ::testing::Test {
protected:
void SetUp() override {
q1_.Enqueue(1);
q2_.Enqueue(2);
q2_.Enqueue(3);
//In this case, TearDown() is not needed since we don't have to clean up after each test, other than what's already done by the destructor.
// void TearDown() override {}
Queue<int> q0_;
Queue<int> q1_;
Queue<int> q2_;
声明完fixture
后,我们可以在接下来的测试中使用它
//第一个参数要是上面声明的类名
TEST_F(QueueTest, IsEmptyInitially) {
EXPECT_EQ(q0_.size(), 0);
TEST_F(QueueTest, DequeueWorks) {
int* n = q0_.Dequeue();
EXPECT_EQ(n, nullptr);
n = q1_.Dequeue();
ASSERT_NE(n, nullptr);
EXPECT_EQ(*n, 1);
EXPECT_EQ(q1_.size(), 0);
//需要主动delete相关对象以回收资源
delete n;
n = q2_.Dequeue();
ASSERT_NE(n, nullptr);
EXPECT_EQ(*n, 2);
EXPECT_EQ(q2_.size(), 1);
delete n;
在上面的调用过程中,GTest
主要做了下面几件事:
构造了一个QueueTest
的实例t1。
调用t1.SetUp()
来初始化。
执行第一个测试用例IsEmptyInitially
。
调用t1.TearDown()
删除实例。
重复以上过程,执行第二个测试用例DequeueWorks
。
测试用例的执行
在Demo1中,我们通过main.cpp
中的RUN_ALL_TESTS()
宏来执行我们定义好的测试用例,但实际上,你可以连接我们第一步编译出的gtest_main
,而不用为每一个项目写一个main
方法。