断言是 gtest 核心组成,所有的测试用例最终都是以断言实现的。
ASSERT 系列的断言如果失败,则会产生一个严重错误并导致当前作用范围的测试用例中断;
EXPECT 不会产生任何错误,测试用例依旧可以继续执行。
致命 非致命 验证
ASSERT_TRUE(condition); EXPECT_TRUE(condition); condition: true
ASSERT_FALSE(condition); EXPECT_FALSE(condition); condition: false
失败时,ASSERT_* 产生一个致命错误,并从当前函数退出,EXPECT_* 则产生一个非致命错误,并允许函数继续运行。
致命断言 非致命断言 验证
ASSERT_EQ(val1, val2); EXPECT_EQ(val1, val2); val1 == val2
ASSERT_NE(val1, val2); EXPECT_NE(val1, val2); val1 != val2
ASSERT_LT(val1, val2); EXPECT_LT(val1, val2); val1 < val2
ASSERT_LE(val1, val2); EXPECT_LE(val1, val2); val1 <= val2
ASSERT_GT(val1, val2); EXPECT_GT(val1, val2); val1 > val2
ASSERT_GE(val1, val2); EXPECT_GE(val1, val2); val1 >= val2
比较 C 字符串,ASSERT_EQ 测试它们是否位于相同内存位置,而不是是否具有相同的值。因此,比较 C 字符串的值使用 ASSERT_STREQ();
当执行指针比较时使用 *_EQ(ptr, nullptr) 和 *_NE(ptr, nullptr) 而不是 *_EQ(ptr, NULL) 和 *_NE(ptr, NULL)。
致命断言 非致命断言 验证
ASSERT_STREQ(str1, str2); EXPECT_STREQ(str1, str2); 具有相同内容
ASSERT_STRNE(str1, str2); EXPECT_STRNE(str1, str2); 具有不同内容
ASSERT_STRCASEEQ(str1, str2); EXPECT_STRCASEEQ(str1, str2); 相同内容忽略大小写
ASSERT_STRCASENE(str1, str2); EXPECT_STRCASENE(str1, str2); 不同内容忽略大小写
如果宽字符串(wchar_t*, Windows 上 UNICODE 模式的 TCHAR*,或 std::wstring)被送进断言,它将在打印时被转换为 UTF-8。
SUCCEED() : 产生一个成功标识,只代表某一个步躁成功;
FAIL() : 产生一个严重错误,相当于一个 ASSERT 宏失败;
ASSERT_THROW(expre,type) : 断言表达式 expre 会抛出一个 type 类型异常;
ASSERTANY THROW(expre) : 断言表达式 expre 会抛出一个任意类型异常;
ASSERT_NO_THROW(expre) : 断言表达式 expre 不会抛出任何异常。
解决场景:有测试场景需要为多个测试复用相同的数据对象配置,可以方便测试
关于测试夹具的实现方式有三种,针对不同的使用场景:
成员函数
虚函数
virtual void SetUp():类似于构造函数,在 TEST_F 之前运行;
virtual void TearDown():类似于析构函数,在 TEST_F 之后运行。
静态函数
static void SetUpTestSuite():在第一个 TEST 之前运行;
static void TearDownTestSuite():在最后一个 TEST 之后运行。
全局事件
继承 testing::Environment
virtual void SetUp():在所有用例之前运行;
virtual void TearDown():在所有用例之后运行。
创建流程:
创建一个公有继承自 ::testing::Test 的类,进行 protected 类内部声明;
编写一个默认的构造函数或 SetUp() 函数为每个 test 准备对象;
编写一个析构函数或 TearDown() 函数释放 SetUp() 中分配的资源;
完善类资源信息,使用 TEST_F 宏,第一个参数填写新建夹具类名称。
TEST_F的使用
使用 TEST_F(类名, 测试夹具名称) :
// TestFixtureName 测试夹具类名(_F:fixture)
TEST_F(TestFixtureName, TestName) {
... test body ...
}
通过 TEST_F() 定义的每个测试,googletest 将在运行时自动创建一个 全新的 测试夹具,立即通过 SetUp() 初始化它,运行测试,最后调用 TearDown() 清理资源,然后删除测试夹具。
测试调用
定义测试后,通过 RUN_ALL_TESTS() 运行它们,如果所有测试都成功,返回 0,否则返回 1。
调用 RUN_ALL_TESTS() 宏时:
CMakeLists.txt
cmake_minimum_required(VERSION 3.0.0)
project(testfixed VERSION 0.1.0)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
//这里指定google的lib和头文件路径,根据自己的实际路径设置
include_directories("~/code/yanjun/google_code/googletest/googletest/include/")
link_directories("~/code/yanjun/google_code/googletest/build/lib")
add_executable(${PROJECT_NAME} testfixed.cc)
target_link_libraries(${PROJECT_NAME} gtest gtest_maingtest_main pthread)
//下面test.cc文件中没有main函数,这里直接连接gtest_maingtest_main库(库里有main函数),也可自己写
test.cc
#include "gtest/gtest.h"
#include
#include
class TestFA : public ::testing::Test {
public:
TestFA() {}
~TestFA() {}
protected:
//以下两个static方法会在每个TEST_F运行前后都会被调用,这里会被调用2次
void SetUp() override {
std::cout << "Reference count: " << deq.use_count() << std::endl;
auto p = deq.get();
auto pxin = *p;
std::deque< int > a(5, -1);
std::cout << "1. size: " << pxin.size() << std::endl;
pxin.swap(a);
std::cout << "2. size:" << pxin.size() << std::endl;
}
void TearDown() override {
deq.reset();
deq = nullptr;
}
//以下两个static方法只会在第一个TEST_F之前和最后一个TEST_F之后被调用一次
static void SetUpTestSuite()
{
std::cout << "SetUpTestSuite --------------------" << std::endl;
}
static void TearDownTestSuite()
{
std::cout << "TearDownTestSuite --------------------" << std::endl;
}
public:
std::shared_ptr< std::deque< int > > deq =
std::make_shared< std::deque< int > >();
};
TEST_F(TestFA, isEmptyOnB) {
EXPECT_GE(deq.use_count(), 1);
if (deq.use_count() != 0) {
deq.reset();
}
}
TEST_F(TestFA, isEmptyOnBIndependence) {
EXPECT_GE(deq.use_count(), 1);
}
分析:
1、创建类public继承自::testing::Test类,并重写SetUp和TearDown方法,在这两个方法里自定义多个测试用例复用的资源创建及释放
2、TEST_F() 定义的每个测试用例,googletest 将在运行时自动创建一个 全新的 测试夹具,立即通过 SetUp() 初始化它,运行测试,最后调用 TearDown() 清理资源,然后删除测试夹具。多个TEST_F()拥有相互独立的个体,数据不会相互影响。
案例2:
继承自testing::Environment类