软件测试:C++ Google Test单元测试框架GTest
目录
- 编译和安装
- 框架使用
- Assertions
- Google Testing
- Google Mocking
- Matchers
- Actions
- 运行结果
最近在写项目的时候,学到了许多关于软件测试的知识,也不断的使用新的测试框架和测试工具,每次总是机械式的拼接其他人的代码,代码发生错误也不知道怎么解决,因此我打算直接将其学一遍,以便为了更灵活的写测试代码
编译和安装
下载地址:Google Test Github网站
然后直接在CMakeLists.txt
引入就好了
include_directories(include) # 设置include路径
set(CXXFLAGS -std=c++11 -lgtest -lpthread)
add_compile_options(${CXXFLAGS})
target_link_libraries(test gtest pthread) # 链接gtest静态库
框架使用
框架的使用主要分为几个模块:Assertions
、Google Testing
、Google Mocking
、Matchers
、Actions
Assertions
这一部分主要是一些断言和比较宏定义,内容较多,以下是一部分,想看全部的可以去看源码
这一部分理解比较简单,不做概述
#define EXPECT_THROW(statement, expected_exception) \
GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_)
#define EXPECT_NO_THROW(statement) \
GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_)
#define EXPECT_ANY_THROW(statement) \
GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_)
#define ASSERT_THROW(statement, expected_exception) \
GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_)
#define ASSERT_NO_THROW(statement) \
GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_)
#define ASSERT_ANY_THROW(statement) \
GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_)
Google Testing
这部分是框架的主要部分,也是开发人员接触最多的地方
首先来介绍三个常用的宏:TEST
、TEST_F
、TEST_P
TEST
:一般测试,在其内部设置断言
TestSuiteName
定义了测试套件名称,TestName
定义了自定义测试名
- 注意这两个的命名不要包括下划线
_
TEST(TestSuiteName, TestName)
{
// statements
}
TEST_F
:多样测试,就是多种不同情况的测试中都会使用相同一份的测试数据的情况
TEST_P(TestFixtureName, TestName)
{
// statements
}
TEST_P
:值参数化测试,自动化产生输入参数进行测试
TEST_P(TestFixtureName, TestName)
{
// statements
}
以TEST_P
为例写一个小demo
首先定义一个测试套件类,在其中定义要产生测试输入参数的类型
// 其中TestWithParam从Test类和WithParamInterface类派生来的,这两个类是必要的
class FooTest : public ::testing::TestWithParam<int> {
};
产生自动化输入参数结构
INSTANTIATE_TEST_CASE_P(InstantiationName, // 此处自定义名称
FooTest, // 测试套件类
::testing::Values(1, 2, 3));
第三个参数是可更改的,比如Range
、Bool
、ValuesIn
、Values
INSTANTIATE_TEST_SUITE_P(
MyInstantiation, MyTestSuite,
testing::Values(...),
[](const testing::TestParamInfo<MyTestSuite::ParamType>& info) {
// Can use info.param here to generate the test suffix
std::string name = ...
return name;
});
要注意的是Combine
,它会把其中的变量组成一个联合排列输入参数(确保支持tr/tuple<T>
特性)
class Bis {
bool Even(int dd) {}
bool Suc(bool dc) {}
};
class CombineTest :
public ::testing::TestWithParam< ::testing::tuple<bool, int> > {
protected:
bool CheckData() {
bool dc = ::testing::get<0>(GetParam());
int dd = ::testing::get<1>(GetParam());
return bis.Suc(dc) && bis.Even(dd);
}
private:
Bis bis;
}
TEST_P(MyCombineTest, CombineTestUnit)
{
EXPECT_TRUE(CheckData());
}
INSTANTIATE_TEST_CASE_P(TestBisValuesCombine, CombineTest, ::testing::Combine(::testing::Bool(), ::testing::Values(3, 4)));
最后在TEST_P
中写测试代码
bool IsPrime(int n)
{
return n > 0;
}
TEST_P(FooTest, DoesBlah)
{
// Inside a test, access the test parameter with the GetParam() method
// of the TestWithParam<T> class:
//在测试中,使用TestWithParam <T>类的GetParam()方法访问测试参数:
int n = GetParam();
EXPECT_TRUE(IsPrime(n));
// ...
}
其他部分API
用的时候直接看官网就好了,主体是这三个API
,其他基本上都是辅助宏
Google Mocking
有点类似于伪装接口,一般情况下用不着,不做概述
Matchers
有点类似于前面的断言,直接看部分源码(在gmok-matchers.h
文件中)
inline PolymorphicMatcher<internal::StrEqualityMatcher<std::wstring>> StrEq(
const std::wstring& str) {
return MakePolymorphicMatcher(
internal::StrEqualityMatcher<std::wstring>(str, true, true));
}
// Matches a string not equal to str.
inline PolymorphicMatcher<internal::StrEqualityMatcher<std::wstring>> StrNe(
const std::wstring& str) {
return MakePolymorphicMatcher(
internal::StrEqualityMatcher<std::wstring>(str, false, true));
}
// Matches a string equal to str, ignoring case.
inline PolymorphicMatcher<internal::StrEqualityMatcher<std::wstring>> StrCaseEq(
const std::wstring& str) {
return MakePolymorphicMatcher(
internal::StrEqualityMatcher<std::wstring>(str, true, false));
}
Actions
这玩意也是一个和断言差不多的东西,看部分源码
template <size_t k, typename Ptr>
struct SaveArgAction {
Ptr pointer;
template <typename... Args>
void operator()(const Args&... args) const {
*pointer = std::get<k>(std::tie(args...));
}
};
template <size_t k, typename Ptr>
struct SaveArgPointeeAction {
Ptr pointer;
template <typename... Args>
void operator()(const Args&... args) const {
*pointer = *std::get<k>(std::tie(args...));
}
};
运行结果
下面来看看一个普通测试套件的运行结果
#include <gtest/gtest.h>
#include <stdint.h>
#include <stdio.h>
int Add(uint32_t a, uint32_t b)
{
return a + b;
}
TEST(LLT, MAIN)
{
EXPECT_EQ(Add(1U, 1U), 1U);
}
int main(int argc, char **argv)
{
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
ubuntu@VM-8-16-ubuntu:~/finale/llt/build$ ./test
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from LLT
[ RUN ] LLT.MAIN
/home/ubuntu/finale/llt/src/test_main.cc:17: Failure
Expected equality of these values:
add(1U, 1U)
Which is: 2
1U
Which is: 1
[ FAILED ] LLT.MAIN (0 ms)
[----------] 1 test from LLT (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[ PASSED ] 0 tests.
[ FAILED ] 1 test, listed below:
[ FAILED ] LLT.MAIN
1 FAILED TEST
这个框架自定义的界面还是很好看的!