03-Google Test使用
1. Google Test简介
Google Test是一种比较方便的C++测试框架, 它能够帮助我们比较方便的进行测试代码的编写, 以及输出尽可能详细的失败信息。能够大大缩短我们测试代码的编写效率, 而且该框架的使用方法也比较简单, 能够降低我们学习新框架的负担。
2. vs2019上Google Test使用
在vs2019中,提供了Google Test项目模板,能够方便的为一个项目添加Google Test。
2.1主要过程
- (1) 创建一个新解决方案,或者打开一个已有解决方案。在解决方案中保证已有一个项目。该项目需要测试的程序,生成.lib或.dll;
- (2)在解决方案中添加一个Google Test项目,添加需测试项目的头文件目录和项目的引用;
- (3)编写测试用例,进行测试;
- (4)运行测试用例。
2.2 Demo
(1)创建一个新项目,设定配置类型为静态库(.lib),目标文件扩展名改为.lib
编写相关项目文件,文件说明如下:
--头文件
--a_cstring.h
--源文件
--a_cstring.cpp
a_cstring.h内容如下:
#ifndef _UTILS_A_CSTRING_HEADER_
#define _UTILS_A_CSTRING_HEADER_
class a_cstring
{
public:
static char* a_strcpy(char* des, const char* src);
static size_t a_strlen(const char* src);
static char* a_strcat(char* des, const char* src);
static int a_strcmp(const char* s1, const char* s2);
};
#endif
a_cstring.cpp内容如下:
#include "a_cstring.h"
/**
* @brief 字符串拷贝函数
* @param des 目标字符串,长度必须要大于等于源字符串的长度
* @param src 源字符串
* @return 目标字符串首地址
*/
char* a_cstring::a_strcpy(char* des, const char* src) {
if (!des || !src) return nullptr;
char* address = des;
while (*src != '\0') {
*des = *src;
des++;
src++;
}
*des = '\0';
return address;
}
/**
* @brief 字符串长度函数.
* @param src 字符串
* @return 字符串长度
*/
size_t a_cstring::a_strlen(const char* src) {
if (!src) return 0;
size_t l = 0;
while (*src != '\0') {
++l;
++src;
}
return l;
}
/**
* @brief 将src字符串追加在des尾部,des和src内存区域需要不重叠,且des必须有足够大的空间来存放src
* @param des 目标字符串
* @param src 源字符串
* @return 尾部追加过源字符串的目标字符串
*/
char* a_cstring::a_strcat(char* des, const char* src) {
if (!des) return nullptr;
if (!src) return des;
char* address = des;
while (*des != '\0') {
des++;
}
while (*src != '\0') {
*des = *src;
des++;
src++;
}
*des = '\0';
return address;
}
/**
* @brief 比较两个字符串
* @param s1 字符串1
* @param s2 字符串2
* @return s1或s2为nullptr,返回-1;s1<s2,返回-1;s1=s2,返回0;s2>s2,返回1;
*/
int a_cstring::a_strcmp(const char* s1, const char* s2) {
if (!s1 || !s2) return -2;
while (*s1 != '\0' && *s2 != '\0') {
if (*s1 < *s2) {
return -1;
}
else if (*s1 == *s2) {
s1++;
s2++;
}
else if (*s1 > * s2) {
return 1;
}
}
if (*s1 == '\0' && *s2 != '\0') {
return -1;
}
else if (*s1 == '\0' && *s2 == '\0') {
return 0;
}
else if (*s1 != '\0' && *s2 == '\0') {
return 1;
}
}
(2)创建Google Test项目并添加对现有项目的引用
Google Test项目创建成功后,会下载对应lib或dll版本的Google Test。会存放在解决方案的根路径下的packages文件夹内。
添加我们要测试的项目的头文件路径
添加项目引用
(3)编写测试用例
默认生成的Google Tset测试项目目录:
其中,pch.h和pch.cpp文件是预编译头,如果不需要可以在test项目右键:属性->C/C+±>预编译头->预编译头修改为关闭。随后删除文件即可。
在test.cpp中编写测试用例(有需要可以建立新的cpp文件)。
#include "pch.h"
TEST(a_strcpy, nullptr) {
char* a = "";
EXPECT_STREQ(nullptr, a_cstring::a_strcpy(nullptr, nullptr));
EXPECT_STREQ(nullptr, a_cstring::a_strcpy(a, nullptr));
EXPECT_STREQ(nullptr, a_cstring::a_strcpy(nullptr, a));
}
TEST(a_strcpy, negative) {
char a[6] = "aaaaa";
char b[2] = "b";
EXPECT_STRNE("baaaa", a_cstring::a_strcpy(a, b));
}
TEST(a_strcpy, positive) {
char a[6] = "aaaaa";
char b[2] = "b";
EXPECT_STREQ("b", a_cstring::a_strcpy(a, b));
}
TEST(a_strlen, nullptr) {
EXPECT_EQ(0, a_cstring::a_strlen(nullptr));
}
TEST(a_strlen, positive) {
EXPECT_EQ(0, a_cstring::a_strlen(""));
EXPECT_EQ(1, a_cstring::a_strlen("a"));
EXPECT_EQ(2, a_cstring::a_strlen("ab"));
}
TEST(a_strcat, nullptr) {
char a[] = "";
EXPECT_STREQ(nullptr, a_cstring::a_strcat(nullptr, nullptr));
EXPECT_STREQ(a, a_cstring::a_strcat(a, nullptr));
EXPECT_STREQ(nullptr, a_cstring::a_strcat(nullptr, a));
}
TEST(a_strcat, positive) {
char a[10] = "";
char b[10] = "bbb";
EXPECT_STREQ("", a_cstring::a_strcat(a, ""));
EXPECT_STREQ("aaa", a_cstring::a_strcat(a, "aaa"));
EXPECT_STREQ("bbb", a_cstring::a_strcat(b, ""));
EXPECT_STREQ("bbbaaa", a_cstring::a_strcat(b, "aaa"));
}
TEST(a_strcmp, nullptr) {
EXPECT_EQ(-2, a_cstring::a_strcmp(nullptr, nullptr));
EXPECT_EQ(-2, a_cstring::a_strcmp("", nullptr));
EXPECT_EQ(-2, a_cstring::a_strcmp(nullptr, ""));
}
TEST(a_strcmp, LT) {
EXPECT_EQ(-1, a_cstring::a_strcmp("", "a"));
EXPECT_EQ(-1, a_cstring::a_strcmp("a", "ab"));
EXPECT_EQ(-1, a_cstring::a_strcmp("ab", "abc"));
}
TEST(a_strcmp, EQ) {
EXPECT_EQ(0, a_cstring::a_strcmp("", ""));
EXPECT_EQ(0, a_cstring::a_strcmp("a", "a"));
EXPECT_EQ(0, a_cstring::a_strcmp("ab", "ab"));
}
TEST(a_strcmp, GT) {
EXPECT_EQ(1, a_cstring::a_strcmp("a", ""));
EXPECT_EQ(1, a_cstring::a_strcmp("ab", "a"));
EXPECT_EQ(1, a_cstring::a_strcmp("abc", "ab"));
}
pch.h内容如下:
#pragma once
#include "gtest/gtest.h"
#include "a_cstring.h"
(4)运行测试用例
在解决方案管理器中点击GoogleTest项目,右键设为启动项目,随后点击运行即可。
或者在菜单栏->测试->测试资源管理器中进行测试。
打印输出如下:
3. GoogleTest的断言/宏
Google Test采用一系列的断言(assertion)来进行代码测试,这些宏有点类似于函数调用。
当断言失败时Google Test将会打印出assertion时的源文件和出错行的位置,以及附加的失败信息,
用户可以直接通过“<<”在这些断言宏后面跟上自己希望在断言命中时的输出信息。
测试宏可以分为两大类:ASSERT_和EXPECT_,这些成对的断言功能相同,但效果不同。其中ASSERT_*将会在失败时产生致命错误并中止当前调用它的函数执行。EXPECT_版本的会生成非致命错误,不会中止当前函数,而是继续执行当前函数。通常情况应该首选使用EXPECT_,因为ASSERT_*在报告完错误后不会进行清理工作,有可能导致内容泄露问题。
3.1 基本断言
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_TRUE(condition) | EXPECT_TRUE(condition) | condition is true |
ASSERT_FALSE(condition) | EXPECT_FALSE(condition) | condition is false |
3.2 二值比较
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
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 |
3.3 字符串比较
Fatal assertion | Nonfatal assertion | Verifies |
---|---|---|
ASSERT_STREQ(str1,str2) | EXPECT_STREQ(str1,str2) | the two C strings have the same content |
ASSERT_STRNE(str1,str2) | EXPECT_STRNE(str1,str2) | the two C strings have different content |
ASSERT_STRCASEEQ(str1,str2) | EXPECT_STRCASEEQ(str1,str2) | the two C strings have the same content, ignoring case |
ASSERT_STRCASENE(str1,str2) | EXPECT_STRCASENE(str1,str2) | the two C strings have different content, ignoring case |
4. 测试用例类型
4.1TEST测试用例
TEST是基本的测试方式, 我们传进测试函数的名字, 调用ASSERT_XX或者EXPECT_XX来测试函数的执行结构和我们预期的是否一致,,demo如下:
TEST(TestCaseName, TestName) {
EXPECT_EQ(1, 1);
EXPECT_TRUE(true);
}
4.2 TEST_F测试用例
有时候我们希望传入的参数是一个class的对象, 并且该对象在传入之前进行了一定的出配置, 并且有好几个函数的测试都需要用到相同的配置, 为了避免重复, Googletest 提供了TEST_F 的测试方式,demo如下:
#include "log.h"
#include "gtest/gtest.h"
class LogTest : public ::testing::Test {
public:
std::string GetLevelStr() {
return mlog::GetLevelStr();
}
protected:
virtual void SetUp()
{
log_ptr = new mlog::Log(mlog::kInfo);
}
virtual void TearDown()
{
delete log_ptr;
}
private:
mlog::Log* log_ptr;
};
TEST_F(LogTest, TestLog)
{
std::string levelStr = GetLevelStr();
std::string expected = "Info";
ASSERT_STRCASEEQ(levelStr.c_str(), expected.c_str());
}