目录
- Value-Parameterized测试
- 示例
- Value-Parameterized实现
- 类型测试
- Type-Parameterized Tests
- 注册test程序
- 获取当前测试程序名
- 运行测试程序选项
- 选择性测试
- 重复测试
- 无序执行
- 分发到不同的机器
- 控制输出
- Controlling How Failures Are Reported
- 结合Sanitizer
Value-Parameterized测试
示例
先看测试代码,需要验证以下三个函数的返回值和1,2,3做对比
int test_p_func1() {
return 1;
}
int test_p_func2() {
return 2;
}
int test_p_func3() {
return 3;
}
用 TEST
这样写
TEST(TestPFuncSuite, TestPFunc1) {
EXPECT_EQ(test_p_func1(), 1);
EXPECT_EQ(test_p_func1(), 2);
EXPECT_EQ(test_p_func1(), 3);
EXPECT_EQ(test_p_func2(), 1);
EXPECT_EQ(test_p_func2(), 2);
EXPECT_EQ(test_p_func2(), 3);
EXPECT_EQ(test_p_func3(), 1);
EXPECT_EQ(test_p_func3(), 2);
EXPECT_EQ(test_p_func3(), 3);
}
这里因为不同的输入(要测试的接口)分别写了EXPECT_EQ,造成代码冗余,为避免这种情况,可以使用Value-parameterized tests。Value-parameterized tests可以通过不同的参数测试功能,避免因为不同的参数而要拷贝多个test body。用Value-parameterized tests可以按如下实现
class TableTestSample1 : public ::testing::TestWithParam<std::function<int()>>
{
public:
void SetUp() override {
std::function<int()> f = GetParam();
val_ = f();
}
protected:
int val_;
};
TEST_P(TableTestSample1, aaa) {
EXPECT_EQ(1, val_);
EXPECT_EQ(2, val_);
EXPECT_EQ(3, val_);
}
INSTANTIATE_TEST_SUITE_P(MyTestPCase1, TableTestSample1, ::testing::Values(&test_p_func1, &test_p_func2, &test_p_func3));
如果test_p_func1带参数,可以通过std::bind将函数对象返回:::testing::Values(std::bind(&test_p_func1, param1, param2));
Value-Parameterized实现
分三步
- 实现一个fixture类,这个类必须要继承自testing::Test 和 testing::WithParamInterface,为简便起见,可以直接派生自testing::TestWithParam(testing::TestWithParam派生自testing::Test 和 testing::WithParamInterface)。注意:
- T可以是任意可以拷贝的类型;
- 如果T是一个裸指针类型,需要对其生命周期管理。
- 使用TEST_P宏定义
- 使用INSTANTIATE_TEST_SUITE_P 宏通过你指定的一系列参数实例化这个test suite
类型测试
假设相同的接口,有不同的实现,想要确保他们满足相同的要求;或者定义了不同的类型,但是它们有着相同的概念(啥意思?)要想验证,这两种情况下对不同的类型有着相同的测试逻辑,如果使用TEST 或者 TEST_F会显得相当冗长,此时可以使用typed tests。这里着重理解下不同的类型有相同的接口
实现步骤:
- 定义一个fixture模板类,继承自::testing::Test
- 关联测试套和要测试的一系列类型,注意这里的类型别名是有必要的
using MyTypes = ::testing::Types<char, int, unsigned int>;
TYPED_TEST_SUITE(FooTest, MyTypes);
- 使用TYPED_TEST()定义typed test suite
测试代码
template<typename T>
class MyClass
{
public:
MyClass() = default;
MyClass(const T &t): val_(t) {
}
T get_param() {
return val_;
}
bool get_name() const {
return true;
}
private:
T val_;
};
template<typename T>
class MyFixture : public ::testing::Test
{
public:
MyFixture() = default;
void SetUp() override {
}
protected:
MyClass<T> p_;
};
using MyTypes = ::testing::Types<char, int, string>;
TYPED_TEST_SUITE(MyFixture, MyTypes);
TYPED_TEST(MyFixture, mytypedtest) {
EXPECT_FALSE(this->p_.get_name()); // 注意,在test suite中要使用this访问Fixture中的变量
}
心得:TYPED_TEST中无法验证实现类型相关的接口信息?
Type-Parameterized Tests
类似于类型测试,但是不要求在测试之前就列出想要测试的类型。可以先写测试逻辑,然后再使用不同的类型实例化。可以在同一程序中多次实例化。
实现步骤:
- 定义fixture模板类,继承自::testing::Test
- 声明类型参数的test suite
- TYPED_TEST_P()实现
- 使用REGISTER_TYPED_TEST_SUITE_P 注册所有要测试的测试程序(第三步的实现),第一个参数是test suite名,其余名字为test name(即为3中的第二个参数)
- 实例化要测试的类型
using MyTypes = ::testing::Types<char, int, unsigned int>;
INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes);
注册test程序
TODO
获取当前测试程序名
实际应用中可能需要知道当前运行的test name,可以通过类TestInfo来获取,要获取TestInfo对象,通过UnitTest单例的current_test_info()获取
// Gets information about the currently running test.
// 注意:不要删除获取到的这个对象,它由UnitTest 这个类管理
const testing::TestInfo* const test_info =
testing::UnitTest::GetInstance()->current_test_info();
printf("We are in test %s of test suite %s.\n",
test_info->name(),
test_info->test_suite_name());
运行测试程序选项
可以通过环境变量或者命令行参数affect测试程序。为了支持命令行参数功能,必须在RUN_ALL_TESTS()
之前调用::testing::InitGoogleTest()
可以通过在测试程序后加–help命令查看支持的选项
如果同时设置和环境变量和命令行参数命令,后者(命令行参数)将有优先权
选择性测试
- –gtest_list_tests:显示出所有要测试的test suite和test name,但不会运行。这个命令可以方便查看编译出来的测试程序支持哪些test suite和test name。显示格式如下:
- gtest默认会运行所有的测试程序,如果只想运行其中的某一部分,可以设置环境变量 GTEST_FILTER 或者 命令行参数 --gtest_filter来设置过滤,gtest将会只运行名字符合过滤器的程序(格式为 TestSuiteName.TestName)。
- ‘*’ 和 ‘?’ 通配符,匹配多个和一个
- ‘:’ 表示或的关系,只要有一个的匹配都可以
- ‘-’ 表示不匹配
./foo_test --gtest_filter=*Null*:*Constructor* Runs any test whose full name contains either "Null" or "Constructor" .
./foo_test --gtest_filter=-*DeathTest.* Runs all non-death tests.
./foo_test --gtest_filter=FooTest.*-FooTest.Bar Runs everything in test suite FooTest except FooTest.Bar.
./foo_test --gtest_filter=FooTest.*:BarTest.*-FooTest.Bar:BarTest.Foo Runs everything in test suite FooTest except FooTest.Bar and everything in test suite BarTest except BarTest.Foo.
-
–gtest_fail_fast:如果期望在第一次测试失败后,停止后面的测试,可以使用该选项
-
临时disable测试:如果有一些测试暂时不需要(有bug或者其他原因),使用注释或者预编译指令#if 0会让这些代码不被编译,可以在test suite名或者test name前加DISABLED_。(在test suite名或者test name前加效果一样)。
// Tests that Foo does Abc.
TEST(FooTest, DISABLED_DoesAbc) { ... }
class DISABLED_BarTest : public testing::Test { ... };
// Tests that Bar does Xyz.
TEST_F(DISABLED_BarTest, DoesXyz) { ... }
- 临时启用disable的测试:通过设置命令函参数–gtest_also_run_disabled_tests或者环境变量GTEST_ALSO_RUN_DISABLED_TESTS 为非0就可以将上面用DISABLE_ disable的test继续运行。
重复测试
偶现的问题可以通过重复多次测试复现,可以通过命令行参数 --gtest_repeat 或者环境变量 GTEST_REPEAT指定test methods运行多少次
–gtest_repeat=-1表示无限次执行
如果包含了global set-up/tear-down,也是会重复执行,为了避免重复执行全局set-up/tear-down,使用–gtest_recreate_environments_when_repeating=false
无序执行
测试套是按顺序执行的,如果想随机无须执行,可以指定命令行参数–gtest_shuffle或者环境变量GTEST_SHUFFLE
gtest会根据当前时间计算出随机种子数,会在控制台上输出。可以通过–gtest_random_seed=SEED或者GTEST_RANDOM_SEED 指定。随机种子数取值范围[0, 99999]。0代表gtest按默认当前时间计算seed值。如果将其与–gtest_repeat=N结合,GoogleTest将选择不同的随机种子,并在每次迭代中重新洗牌测试。
分发到不同的机器
控制输出
- 通过带颜色的终端输出可以比较清晰的分辨信息,可以通过GTEST_COLOR 或者命令行参数–gtest_color,取值为yes,no,auto(默认值)使能颜色或由gtest决定。
- 压缩测试结果:默认情况下一条测试会输出一条输出表明是否成功,通过命令行参数–gtest_brief=1或者将环境变量GTEST_BRIEF设置为1可以简化输出
- 禁止显示时间:more情况下会显示运行每条test花费了多长时间,通过命令行参数–gtest_print_time=0或者将GTEST_PRINT_TIME 设置为0禁止显示
- Suppressing UTF-8 Text Output
- 生成XML报告:通过将环境变量GTEST_OUTPUT 或者命令行参数 --gtest_output设置为"xml:path_to_output_file",会将xml文件生成在指定位置。也可以直接设置为"xml",会在当前目录下输出test_detail.xml。如果指定目录如"xml:output/directory/",会在指定目录下生成 测试程序名.xml,如果文件存在,为避免覆盖,googletest会选择一个不同的名字如 测试程序名_1.xml。
- 生成json报告:同生成xml,通过将环境变量GTEST_OUTPUT 或者命令行参数 --gtest_output设置为"json:path_to_output_file",也可以直接设置为"json",会在当前目录下输出test_detail.json。
Controlling How Failures Are Reported
TODO
结合Sanitizer
TODO
Undefined Behavior Sanitizer:https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
Address Sanitizer:https://github.com/google/sanitizers/wiki/AddressSanitizer
Thread Sanitizer:https://github.com/google/sanitizers/wiki/ThreadSanitizerCppManual