gTest 学习

  • Post author:
  • Post category:其他


gTest


目录


在Clion上安装


断言


判断bool


数值判断


字符串判断


显示返回成功或失败


异常检查


输出更详细信息


自定义输出


浮点数相等


相近判断


事件


全局事件


TestSuite事件


TestCase事件


参数化


在Clion上安装

下载后解压到项目

配置cmake

cmake_minimum_required(VERSION 3.16)
project(myTest)

message("------------ Options -------------")
message("  CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
message("  CMAKE_BUILD_TYPE: Hello World!")

# 编译google test,会在当前目录生成libtest.a静态库
add_subdirectory(./googletest)
#头文件
include_directories(${PROJECT_SOURCE_DIR}/src/include ./googletest/include)

#源文件
aux_source_directory(${PROJECT_SOURCE_DIR}/src/main dir_srcs)
#库文件 : libtest.a 添加到链接路径中
link_directories(${PROJECT_SOURCE_DIR}/lib ${PROJECT_SOURCE_DIR}/googletest)

#需要添加googletest运行需要的pthread
set(LIBRARIES pthread)

set(CMAKE_CXX_STANDARD 11)

add_executable(myTest main.cpp)
target_link_libraries(${PROJECT_NAME} gtest)

测试点

TEST(TestSuiteName,TestCaseName)

最简单的

#include <iostream>
#include <gtest/gtest.h>


int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

在main的上面空行(不一定空行,其实只要不在方法里都行),点Run all运行全部

就会产生底下这个

如果运行main的话

断言

ASSERT_* 系列的断言,当检查点失败时,退出当前函数(注意:并非退出当前案例)。

EXPECT_* 系列的断言,当检查点失败时,继续往下执行。

可以通过ASSERT_*<<“msg”; 输出msg

判断bool

ASSERT_* EXPECT_* 功能
ASSERT_TRUE(condition) EXPECT_TRUE(condition) condition是否为true
ASSERT_FALSE(condition) EXPECT_FALSE(condition) condition是否为false

数值判断

ASSERT_* EXPECT_* 功能
ASSERT_EQ(expect,actual) EXPECT_EQ(expect,actual) expect==actual
ASSERT_NE(expect,actual) EXPECT_NE(expect,actual) expect!=actual
ASSERT_LT(expect,actual) EXPECT_LT(expect,actual) expect<actual
ASSERT_LE(expect,actual) EXPECT_LE(expect,actual) expect<=actual
ASSERT_GT(expect,actual) EXPECT_GT(expect,actual) expect>actual
ASSERT_GE(expect,actual) EXPECT_GE(expect,actual) expect>=actual

字符串判断

ASSERT_* EXPECT_* 功能
ASSERT_STREQ(expect,actual) EXPECT_STREQ(expect,actual) 字符级别相等
ASSERT_STRNE(expect,actual) EXPECT_STRNE(expect,actual) 字符级别不等
ASSERT_STRCASEEQ(expect,actual) EXPECT_STRCASEEQ(expect,actual) 忽略大小写后,字符级别相等
ASSERT_STRCASENE(expect,actual) EXPECT_STRCASENE(expect,actual) 忽略大小写后,字符级别不等

显示返回成功或失败

显示返回成功SUCCEED()

ASSERT版本:  FAIL()

EXPECT版本:  ADD_FAILURE()

异常检查

ASSERT_* EXPECT_* 功能
ASSERT_THROW(statement,expection_type) EXPECT_THROW(statement,expection_type) statement抛出的异常类型为expection_type
ASSERT_ANY_THROW(statement) EXPECT_ANY_THROW(statement) statement抛出的任意类型异常
ASSERT_NO_THROW(statement) EXPECT_NO_THROW(statement) statement不抛异常

输出更详细信息

ASSERT_* EXPECT_* 功能
ASSERT_PRED1(pred,val1) EXPECT_PRED1(pred,val1) pred(val1)是否输出true
ASSERT_PRED2(pred,val1,val2) EXPECT_PRED2(pred,val1,val2) pred(val1,val2)是否输出true

以此类推,到ASSERT_PRED5

自定义输出

ASSERT_* EXPECT_* 功能
ASSERT_PRED_FORMAT1(pred,val1) EXPECT_PRED_FORMAT1(pred,val1) pred(val1)是否输出true
ASSERT_PRED_FORMAT2(pred,val1,val2) EXPECT_PRED_FORMAT2(pred,val1,val2) pred(val1,val2)是否输出true

以此类推,到ASSERT_PRED_FORMAT5

浮点数相等

ASSERT_* EXPECT_* 功能
ASSERT_FLOAT_EQ(expect,actual) EXPECT_FLOAT_EQ(expect,actual) expect==actual
ASSERT_DOUBLE_EQ(expect,actual) EXPECT_NE(expect,actual) expect!=actual

相近判断

ASSERT_NEAR(

val1, val2, abs_error

) :|val1-val2|<=abs_error

#include <iostream>
#include <gtest/gtest.h>
#include <string>

int add(const int& a,const int& b){return a+b;}

TEST(TEST_SUCCESS,TEST1){
    EXPECT_EQ(3,add(1,2)); //判断相等
}

TEST(TEST_FAILED,TEST1){
    EXPECT_EQ(4,add(1,2));
}

int x[]={1,2,3,4};
int y[]={1,3,2,4};

TEST(TEST_ARRAY,TEST1){
    for(int i=0;i<4;++i){
        EXPECT_EQ(x[i],y[i]);
    }
}

TEST(TEST_ARRAY,TEST2){
    for(int i=0;i<4;++i){
        EXPECT_EQ(x[i],y[i])<< "Vectors x and y differ at index " << i;
    }
}

TEST(string_cmp_test,test1){
    const char* pszCoderZh="CoderZh";

    const wchar_t* wszCoderZh=L"CoderZh";

    std::string strCoderZh="CoderZh";
    std::wstring wstrCoderZh=L"CoderZh";

    EXPECT_STREQ("CoderZh",pszCoderZh);//判断字符串相等
    EXPECT_STREQ(L"CoderZh",wszCoderZh);

    EXPECT_STRNE("CnBlogs",pszCoderZh);//判断字符串不等
    EXPECT_STRNE(L"CnBlogs",wszCoderZh);

    EXPECT_STRCASEEQ("coderzh",pszCoderZh);//判断字符串忽略大小写后相等
    //EXPECT_STRCASEEQ(L"coderzh",?wszCoderZh);????不支持

    EXPECT_STREQ("CoderZh",strCoderZh.c_str());
    EXPECT_STREQ(L"CoderZh",wstrCoderZh.c_str());
}

TEST(string_cmp_test,test2){
    std::string str="敲你马";
//    std::wstring wstr=L"敲你马";  //不知道为什么崩了

    EXPECT_STREQ("敲你马",str.c_str());
//    EXPECT_STREQ(L"敲你马",wstr.c_str());
}

TEST(explicit_test,test1){
    ADD_FAILURE()<<"failed";//返回失败

//    FAIL();

    SUCCEED();//直接返回成功
}

int gcd(int a,int b){
    if(a==0||b==0)throw "don't do that";
    int c=a%b;
    while(c){
        a=b;
        b=c;
        c=a%b;
    }
    return b;
}

TEST(foo_test,handle_zero_input){
    EXPECT_ANY_THROW(gcd(0,5)); //期待抛出任何异常
    EXPECT_THROW(gcd(5,0),const char*); //抛出指定类型异常
}

bool mutuallyPrime(const int& a,const int& b){
    return gcd(a,b)==1;
}

TEST(predicate_assert_test,test1){
    EXPECT_PRED2(mutuallyPrime,5,10); //期待mutuallyPrime(5,4)返回true

}

testing::AssertionResult AssertFoo(const char* m_expr, const char* n_expr, const char* k_expr, int m, int n, int k) {
    const int result=gcd(m,n);
    if (result == k)
        return testing::AssertionSuccess();
    testing::Message msg;
    msg << m_expr << "?和?" << n_expr << "?的最大公约数应该是:" << result << "?而不是:" << k_expr;
    return testing::AssertionFailure(msg);
}

TEST(AssertFooTest, HandleFail){
    EXPECT_PRED_FORMAT3(AssertFoo, 3, 6, 2); //自定义错误输出
}

TEST(FloatTest,Test1){
    EXPECT_FLOAT_EQ(1.6f,5.0f/2);
}
TEST(FloatTest,Test2){
    EXPECT_DOUBLE_EQ(1.6,5.0/2);
}
TEST(FloatTest,Test3){
    EXPECT_PRED_FORMAT2(testing::FloatLE, 1.6f,5.0f/2);
    EXPECT_PRED_FORMAT2(testing::DoubleLE, 1.6,5.0/2);
}

TEST(NearTest,Test1){
    EXPECT_NEAR(1,2,1);
}

//template <typename T>
//class FooType {
//public:
//    void Bar() {
//        testing::StaticAssertTypeEq<int, T>();
//    }
//};
//
//TEST(TypeAssertionTest,Demo)
//{
//    FooType<bool>fooType;
//    fooType.Bar();
//}

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

事件

全局事件

SetUp()方法在所有案例执行前执行

TearDown()方法在所有案例执行后执行

需要继承testing::Environment,并在main中使用testing::AddGlobalTestEnvironment(你的类);

进行注册

如果有多个,你可以注册多个

TestSuite事件

我们需要写一个类,继承

testing::Test,然后实现两个静态方法

SetUpTestCase:每个TestSuite执行前执行

TearDownTestCase:每个TestSuite执行后执行

测试点不再使用TEST,而是TEST_F,第一个参数传你的类,第二个传test case名字

TestCase事件

我们需要写一个类,继承testing::Test,然后实现两个静态方法

SetUp:每个TestCase执行前执行

TearDown:每个TestCase执行后执行

测试点不再使用TEST,而是TEST_F,第一个参数传你的类,第二个传test case名字

#include <iostream>
#include <gtest/gtest.h>
#include <string>

//要有test case才会执行setup和tear down
class FooEnvironment:public testing::Environment{
public:
    void SetUp() override{  //在所有案例执行前执行
        std::cout<<"Foo FooEnvironment setUp"<<std::endl;
    }
    void TearDown() override{  //方法在所有案例执行后执行
        std::cout<<"Foo FooEnvironment tearDown"<<std::endl;
    }
};

class FooTest:public testing::Test{
protected:
    static void SetUpTestCase(){  //第一个TestCase之前执行
        a=new int[10];
        for(int i=0;i<10;++i)a[i]=i;
        std::cout<<"init"<<std::endl;
    }
    static void TearDownTestCase(){  //在最后一个TestCase之后执行
        delete[] a;
        a= nullptr;
        std::cout<<"destroy"<<std::endl;
    }

    static int* a;
};

int* FooTest::a= nullptr;

//使用TEST_F这个宏,第一个参数必须是我们上面类的名字
TEST_F(FooTest,test1){
    for(int i=0;i<10;++i){
        EXPECT_EQ(i,a[i]);
    }
}

TEST_F(FooTest,test2){
    for(int i=0;i<10;++i){
        EXPECT_EQ(i-1,a[i]);
    }
}

class FooTest2:public testing::Test{
public:
    void SetUp() override {  //在每个TestCase之前执行
        std::cout<<"start case"<<std::endl;
    }
    void TearDown() override {  //在每个TestCase之后执行
        std::cout<<"end case"<<std::endl;
    }
};

//使用TEST_F这个宏,第一个参数必须是我们上面类的名字
TEST_F(FooTest2,test1){
    SUCCEED();
}

int main(int argc, char **argv)
{
    testing::AddGlobalTestEnvironment(new FooEnvironment);
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

参数化

写一个类,继承

testing::TestWithParam<T>,这个T就是测试的类型

有个类型参数化没怎么看懂,以后再说

INSTANTIATE_TEST_SUITE_P(prefix,test_suite_name,…)

第一个参数是前缀,

第二个参数是名称,就是你的类名

第三个是参数生成器

参数生成器 功能
Values(a,b,…) 依次取a,b,…
ValuesIn(container) 依次取容器中的值
ValuesIn(begin, end) 依次取begin在end中的值
Bool() 生成false,true
Range(start,end,step) 类似py的range
Combine(g1,g2,…) 组合生成器g1,g2,…,返回tuple<类型1,类型2,…>,需要#includ<tuple>

如果需要事件(如teardown)可以直接在类中写,不用继承testing::test以及使用TEST_F

类型参数化,没怎么看懂,以后再说

#include <iostream>
#include <gtest/gtest.h>
#include <string>
#include <vector>
#include <tuple>

bool isPrime1(int x){
    if(x<2)return false;
    if(x==2||x==3)return true;
    if(x%6!=1&&x%6!=5)return false;
    for(int i=5;i*i<=x;i+=6){
        if(x%i==0||x%(i+2)==0)return false;
    }
    return true;
}

bool isPrime2(int x){
    if(x<2)return false;
    for(int i=2;i*i<=x;++i){
        if(x%i==0)return false;
    }
    return true;
}

class IsPrimeParamTest:public testing::TestWithParam<int>{
};
//INSTANTIATE_TEST_CASE_P(TrueReturn, IsPrimeParamTest, testing::Values(3, 5, 11, 23, 17));

//第一个参数是测试案例的前缀,可以任意取,但是当第二个参数相同时,第一个参数必须不同
//第二个参数是测试案例的名称,需要和之前定义的参数化的类的名称相同,如:IsPrimeParamTest
//第三个参数是可以理解为参数生成器,上面的例子使用test::Values表示使用括号内的参数。Google提供了一系列的参数生成的函数:
//Values(a,b,c,...)依次取a,b,c...
INSTANTIATE_TEST_SUITE_P(TrueReturn1, IsPrimeParamTest, testing::Values(3, 5, 11, 23, 17));


//Range,类似py的range
INSTANTIATE_TEST_SUITE_P(TrueReturn2,IsPrimeParamTest,testing::Range(1,10001,1));


int a[]{3,5,11,23,17};
//ValuesIn,容器
INSTANTIATE_TEST_SUITE_P(TrueReturn3,IsPrimeParamTest,testing::ValuesIn(a));

//ValuesIn,迭代器
std::vector<int> v{1,2,4,5,6};
INSTANTIATE_TEST_SUITE_P(TrueReturn4,IsPrimeParamTest,testing::ValuesIn(v.begin(),v.end()));


TEST_P(IsPrimeParamTest,test1){
    int n=GetParam();
    ASSERT_EQ(isPrime2(n),isPrime1(n));
}


class BoolTest:public testing::TestWithParam<bool>{
};
//Bool,返回true,false
INSTANTIATE_TEST_SUITE_P(TrueReturn1,BoolTest,testing::Bool());
TEST_P(BoolTest,test1){
    bool n=GetParam();
    ASSERT_EQ(true,n);
}

class CombineTest:public testing::TestWithParam<std::tuple<int,int,int> >{

};
//Combine,进行排列组合,里面每个参数都是一个参数生成器(也许能套娃,但是我懒得试了),GetParam会返回tuple
//需要#include<tuple>
INSTANTIATE_TEST_SUITE_P(
        TrueReturn1,
        CombineTest,
        testing::Combine(
                testing::ValuesIn(a),
                testing::ValuesIn(v),
                testing::Range(1,5)));
TEST_P(CombineTest,test1){
    std::tuple<int,int,int> p=GetParam();
    //用std::get<0>获取第0个参数
    EXPECT_NE(std::get<0>(p)+std::get<1>(p),std::get<2>(p)+std::get<1>(p));
}


//还有个类型参数化,没怎么看懂,以后再说

int main(int argc, char **argv)
{
    testing::InitGoogleTest(&argc, argv);

    return RUN_ALL_TESTS();
}

运行参数

–gtest_list_tests 使用这个参数时,将不会执行里面的测试案例,而是输出一个案例的列表
–gtest_filter

对执行的测试案例进行过滤,支持通配符

?    单个字符

*    任意字符

–    排除,如,-a 表示除了a

:    取或,如,a:b 表示a或b

–gtest_repeat=[COUNT]

-1 无限次

–gtest_color=(yes|no|auto) 输出命令行时是否使用一些五颜六色的颜色。默认是auto,这个好像在clion的控制台没有什么卵用
–gtest_print_time 输出命令行时是否打印每个测试案例的执行时间。默认是不打印的。但是好像clion中,是默认打印的
–gtest_output=xml[:DIRECTORY_PATH\|:FILE_PATH]

将测试结果输出到一个xml中

不指定输出路径时,默认为案例当前路径。

–gtest_break_on_failure 当案例失败时停止

其他的再说吧

有两种方法,一种是在main中,用testing::GTEST_FLAG(),

如gtest_list_tests,把gtest这个前缀去掉,放括号里

int main(int argc, char **argv)
{
    //--gtest_list_tests
    testing::GTEST_FLAG(list_tests) = "";
    testing::InitGoogleTest(&argc, argv);

    return RUN_ALL_TESTS();
}

另一种是直接在clion中指定

参考


https://www.cnblogs.com/coderzh/archive/2009/04/06/1426755.html



版权声明:本文为qq_39942341原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。