Why?
产品级的代码写出来肯定是要进行很多测试的,像“程序员酒吧”那种情况,肯定是不允许的。写完代码,测试,找bug修bug的过程非常麻烦。
所以,GoogleTest官方的介绍是:
GoogleTest helps you write better C++ tests.
Google眼中好的测试应该有以下特点:
- 测试间相互独立,互不影响
- 测试应该能很好反应被测代码的结构
- 测试应该是可移植,可复用,可跨平台的
- 当测试失败时,测试应该能尽可能多地提供问题信息
- 测试应该具有很高的自动化程度,避免无效劳动
- 测试应该够快,性能影响较小。
What?
GoogleTest基于xUnit实现。
注意不同命名,Google Test和大部分教科书的术语称呼方式不太一样,GoogleTest中的Test对应教科书中的Test Case
最简单的测试,就是写断言,通过就接着运行,不通过就终止。
一个测试单元包括多个测试(断言),称为test suite,当多个测试需要共享一个对象或者子程序时,可以把他们放到一个 test fixture class种,
一个测试程序包括多个测试单元。
Assertions断言
类似于宏,当出错时会打印Assert所在的源文件以及行号,错误信息
分为:
- ASSERT_*:出错就会结束当前函数
- 注意,崩溃后就会直接退出,不会进行内存清理,有可能导致内存泄漏
- EXPECT_*:出错后输出错误信息,但是不会结束,接着执行
Example:
ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";
for (int i = 0; i < x.size(); ++i) {
EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i;
}
后面的是自定义报错信息,通过<<输出,能输出给ostream的 ,都能输出。
GoogleTest提供了很多不同类型的断言,例如是否相等,比大小,浮点数值,字符串值等等。
官方文档
Test
使用TEST()
宏定义一个测试函数,该测试函数没有返回值。
TEST(TestSuiteName, TestName) {
... test body ...
}
第一个参数是所属Test Suite的名字,第二个是测试本身的名字,命名方式和函数命名方式相同,二者都不应该带_
,不同Test Suite下的测试可以有相同的名字。
Google Test以Test Suite的测试结果进行统计,所以同一个Test Suite下的测试,应该有相同的输入。
Test Fixture
fixture:固定装置,卡具,固定附着物
怎么翻译?测试夹具?
当多个测试需要相同输入的时候,可以采用Test fixture,可以保证为不同测试配置相同的输入
不要使用全局变量来实现相同输入,有可能前一个测试的变动导致后一个测试的失败,但是问题并不在后一个测试,调试起来更麻烦,Test fixture保证不同测试的输入名称相同,配置相同,但并不是一个对象,不会导致这种情况。
配置fixture方式:
- 继承
testing::Test
类,并将成员设为protected - 在这个类中声明想用的对象
- 如果该对象需要初始化,可以写一个构造函数或者
SetUp()
函数 - 如果需要析构,可以写一个析构函数,或者
TearDown()
函数,
当使用fixture时,使用TEST_F
而不是TEST,TEST_F可以访问fixture的成员对象和成员函数。TEST_F的第一个参数填fixture的名字,而不是Suite的名字
TEST_F(TestFixtureClassName, TestName) {
... test body ...
}
Example:
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;
...
};
class QueueTest : public testing::Test {
protected:
QueueTest() {
// q0_ remains empty
q1_.Enqueue(1);
q2_.Enqueue(2);
q2_.Enqueue(3);
}
// ~QueueTest() override = default;
Queue<int> q0_;
Queue<int> q1_;
Queue<int> q2_;
};
TEST_F(QueueTest, IsEmptyInitially) { //在配置TEST_F前需要先声明好fixture,否则会报错
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 n;
n = q2_.Dequeue();
ASSERT_NE(n, nullptr);
EXPECT_EQ(*n, 2);
EXPECT_EQ(q2_.size(), 1);
delete n;
}
调用测试
TEST和TEST_F都会隐式在Google Test中注册,不需要再显式调用,只需要在所有定义完后执行RUN_ALL_TESTS()宏就可以执行全部测试,包括不同源文件中的测试。
注意,main函数返回必须返回RUN_ALL_TEST(),因为Google TEST判断一个测试是否通过是根据返回的GoogleTest flags而不是终端输出,所以必须返回,不返回会报错。且RUN_ALL_TESTS()只能调用一次,多次调用也会出问题。
Example
官方文档中给出的一个案例:
#include "this/package/foo.h"
#include <gtest/gtest.h>
namespace my {
namespace project {
namespace {
// The fixture for testing class Foo.
class FooTest : public testing::Test {
protected:
// You can remove any or all of the following functions if their bodies would
// be empty.
FooTest() {
// You can do set-up work for each test here.
}
~FooTest() override {
// You can do clean-up work that doesn't throw exceptions here.
}
// If the constructor and destructor are not enough for setting up
// and cleaning up each test, you can define the following methods:
void SetUp() override {
// Code here will be called immediately after the constructor (right
// before each test).
}
void TearDown() override {
// Code here will be called immediately after each test (right
// before the destructor).
}
// Class members declared here can be used by all tests in the test suite
// for Foo.
};
// Tests that the Foo::Bar() method does Abc.
TEST_F(FooTest, MethodBarDoesAbc) {
const std::string input_filepath = "this/package/testdata/myinputfile.dat";
const std::string output_filepath = "this/package/testdata/myoutputfile.dat";
Foo f;
EXPECT_EQ(f.Bar(input_filepath, output_filepath), 0);
}
// Tests that Foo does Xyz.
TEST_F(FooTest, DoesXyz) {
// Exercises the Xyz feature of Foo.
}
} // namespace
} // namespace project
} // namespace my
int main(int argc, char **argv) {
testing::InitGoogleTest(&argc, argv); //初始化函数,必须调用
return RUN_ALL_TESTS();
}
Google Test设计是线程安全的,目前在pthread库上是安全的,但是在其他系统的其他线程库上(例如Windows)不确定。
How?
如果使用cmake进行工程管理,可以使用cmake fetch模块添加依赖库地址,然后就可以使用了。
也可以使用find_package或者find_library或者git submodule等方式,总之就是把gtest列成依赖并使cmake能找到库文件就可以了
CMakeList文件:
cmake_minimum_required(VERSION 3.14)
project(my_project)
# GoogleTest requires at least C++14
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(FetchContent) # 包括FetchContent模块,该模块用于下载依赖项
FetchContent_Declare( # 声明一个依赖
googletest
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(gtest_force_shared_crt ON CACHE BOOL "" FORCE)
# 设置gtest_force_shared_crt为ON,防止覆盖父项目的编译器/链接器设置
FetchContent_MakeAvailable(googletest)
enable_testing() # 启用测试
add_executable(tests main.cpp) # 添加一个可执行文件
target_link_libraries(tests gtest_main) # 链接gtest_main库文件
include(GoogleTest) # 包括GoogleTest模块
gtest_discover_tests(tests) # 使用gtest_discover_tests函数将测试用例添加到测试中
main.cpp:
#include <iostream>
#include <gtest/gtest.h>
int add(int a, int b) {
return a + b;
}
TEST(AddTest, Positive) {
EXPECT_EQ(2, add(1, 1));
EXPECT_EQ(3, add(1, 2));
EXPECT_EQ(4, add(2, 2));
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}