Google Test In Action
Part 1
Google C++ Testing Framework
Friday,August 19,2011
(注:插入图片过程复杂,涉及图片的都Ignore.)
目录
1. Introduction. 2
2. Setting. 2
3. Demo. 3
4. Assertions. 4
5. Parameterized. 9
6. Global Set-Up and Tear-Down. 10
7. Other. 11
8. Google C++ Testing Framework Samples. 12
9. UI 16
1. Introduction
第一部分已省略,详情见googletest官方网站:
http://code.google.com/p/googletest/
2. Setting
1) 首先登录googletest官方网站下载相关的项目:
http://code.google.com/p/googletest/downloads/detail?name=gtest-1.6.0.zip
2) 将下载的gtest-1.6.0.zip解压,将得到如下的结构:
点击进入gtest-1.6.0\ msvc,用Visual Studio打开gtest.sln,进行编译,将会得到相应的gtestd.lib/gtest.lib文件(gtestd.lib对应Debug模式,gtest.lib对应Release模式)。
3) 配置相应的Visual Studio环境,配置路径如下:
Property Pages / Configuration Properties / VC++ Directories / Include Directories
Property Pages / Configuration Properties / VC++ Directories / Library Directories
Property Pages / C/C++ /Code Generation Runtime Library
Property Pages / Linker / Input /Additional Dependencies (Debug / Release 下的 * . lib)
注:刚开始我忘了设置相应的lib文件 ,则编译阶段可以通过,但在连接阶段会抛出如下错误:
3. Demo
首先做一个简单的Demo,以了解大统的流程,使用Google Test 做C++ 单元测试只要简单的三步,如下:
1) 添加要测试相关的相应头文件,和gtest.h头文件以引入testing framework:
#include "gtest/gtest.h"
2) 编写所要测试的方法
int Func(int a, int b)
{
if (a <= 0 || b <= 0)
throw "my god!";
return a + b;
}
3) 使用TEST宏去定义一个Test Case,不再需要写类似jUnit的runTest,test****()之类的方法,
Test宏的用法如下:
TEST(test_case_name, test_name) {
... test body ...
}
如:
TEST(FuncTest, HandleNoneZeroInput)
{
EXPECT_EQ(2, Func(1,1));
EXPECT_EQ(6, Func(3,4));
}
该Test Case用于测试EXPECT_EQ()的两个参数是否相等,在下节进行具体的讲解。
4) 在main函数中调用InitGoogleTest(),RUN_ALL_TESTS(),以运行所有的TEST.
int _tmain(int argc, _TCHAR* argv[])
{
std::cout<< "Running main() from glibtest.cpp ....\n";
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
5) 运行Test ,如下图所示:
如果出现的都是[OK],则表示测试通过,否则将出现红色标志的[FAILED]表示该测试失败。
一个简单的Demo就结束了, 应该对Google Test有了一个大致的了解。
4. Assertions
Google Test 提供了两种断言机制,分别是ASSERT_*和EXPECT_*,我们可以把这两种的区别用C++里面的两个关键字来解释:continue,break;
ASSERT_* : “break”, 当检查点失败时,退出当前函数。
EXPECT_* : “continue”, 当检查点失败时,还会往下执行。
· 两个数值之间的比较
|
Fatal assertion |
Nonfatal assertion |
Verifies |
|
ASSERT_EQ(expected, actual); |
EXPECT_EQ(expected, actual); |
expected == actual |
|
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 |
例如,对于[ASSERT| EXPECT]_EQ这个检查点用来检验第一个参数和第二个参数是否相等,第一个参数是你期望的值(expected),第二个参数是实际计算出来的值(actual)。
Ø 如果检查点是:EXPECT_EQ(2, Func(1,1)); 则相应的测试结果为:
表示期望值和实际值相等。[Func(int,int)在上面已经定义]
Ø 如果检查点是:EXPECT_EQ(6, Func(3,4)); 则相应的测试结果为:
表示期望值和实际值不等。
Ø 如果用于测试vector和array里面的各个元素,则如下所示(可以将vector理解为array的现代版,所以在此只对vector做一个Demo):
// create a vector vec1 with 3 elements of value 1
vector <int> vec1 (3, 1);
// copy contructor : copy from vec1
vector <int> vec2 (vec1);
// create a vector vec3 with 3 elements of value 2
vector <int> vec3 (3, 2);
TEST(EqTest,HandleNoneZeroInput)
{
for(vector<int>::size_type i = 0; i < vec1.size(); ++i)
{
EXPECT_EQ(vec1[i],vec2[i]);
EXPECT_EQ(vec1[i],vec3[i]);
}
}
测试结果如下:
如图所示,没有指出vector里面的相应元素值,如果要显示具体的数值,则可以重载<<来输出具体的数值:
ostream& operator<< (ostream& os,const string& str)
{
return os << str;
}
EXPECT_EQ(vec1[i],vec3[i]) << "Vectors x and y differ at index " << i;
如图所示,现在可以正确输出vector里面具体的数值。
· 字符串之间的比较
|
Fatal assertion |
Nonfatal assertion |
Verifies |
|
ASSERT_STREQ(expected_str, actual_str); |
EXPECT_STREQ(expected_str, actual_str); |
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(expected_str, actual_str); |
EXPECT_STRCASEEQ(expected_str, actual_str); |
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 |
仍以简单的[ASSERT| EXPECT]_ STREQ和[ASSERT| EXPECT]_ STRNE这个检查点进行介绍:
char *pChar = "Company";
wchar_t *pWChar = L"Company";
std::string str = "Company";
std::wstring wstr = L"Company";
TEST(StringCmpareTest,Demo)
{
// *_STREQ : Verify the two C Strings have the same content
EXPECT_STREQ("Company",pChar);
EXPECT_STREQ(L"Company",pWChar);
// *_STRNE : Verify the two C strings have different content
EXPECT_STRNE("Company_",pChar);
EXPECT_STRNE(L"Company_",pWChar);
EXPECT_STREQ("Company",str.c_str()); //c_str : c-style <-> C++
EXPECT_STREQ(L"Company",wstr.c_str());
}
运行结果如下:
· 异常断言
|
Fatal assertion |
Nonfatal assertion |
Verifies |
|
ASSERT_THROW(statement, exception_type); |
EXPECT_THROW(statement, exception_type); |
statement throws an exception of the given type |
|
ASSERT_ANY_THROW(statement); |
EXPECT_ANY_THROW(statement); |
statement throws an exception of any type |
|
ASSERT_NO_THROW(statement); |
EXPECT_NO_THROW(statement); |
statement doesn't throw any exception |
直接上例子,如下:
TEST(FuncTest,HandleZeroInput)
{
EXPECT_ANY_THROW(Func(10,0));
EXPECT_THROW(Func(0,5),char*);
}
第一个检查点期望语句抛出了一个任意的异常,第二个检查点期望语句抛出一个指定类型的异常。结果如下:
如果我们改一下上面的TEST,改成:
TEST(FuncTest,HandleZeroInput)
{
EXPECT_ANY_THROW(Func(10,3));
EXPECT_THROW(Func(1,5),int*);
}
两个检查点都没有按预期的抛出异常,则出现如下结果:
· Windows HRESULT 断言
对于Windows HRESULT 断言的Demo提供在ATL_COM项目中,由于对COM了解不够深入,在此只是简单的进行了演示(代码来自于gtest_unittest.cc第3914行开始,为了演示进行了简化):
static HRESULT UnexpectedHRESULTFailure() {
return E_UNEXPECTED;
}
static HRESULT OkHRESULTSuccess() {
return S_OK;
}
static HRESULT FalseHRESULTSuccess() {
return S_FALSE;
}
TEST(HRESULTAssertionTest, EXPECT_HRESULT_SUCCEEDED) {
EXPECT_HRESULT_SUCCEEDED(S_OK);
EXPECT_HRESULT_SUCCEEDED(S_FALSE);
}
TEST(HRESULTAssertionTest, ASSERT_HRESULT_SUCCEEDED) {
ASSERT_HRESULT_SUCCEEDED(S_OK);
ASSERT_HRESULT_SUCCEEDED(S_FALSE);
}
TEST(HRESULTAssertionTest, EXPECT_HRESULT_FAILED) {
EXPECT_HRESULT_FAILED(E_UNEXPECTED);
}
TEST(HRESULTAssertionTest, ASSERT_HRESULT_FAILED) {
ASSERT_HRESULT_FAILED(E_UNEXPECTED);
}
测试结果如下:
在google的源码中,{ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr)的定义是这样的:
#if GTEST_OS_WINDOWS
// Macros that test for HRESULT failure and success, these are only useful
// on Windows, and rely on Windows SDK macros and APIs to compile.
//
// * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr)
//
// When expr unexpectedly fails or succeeds, Google Test prints the
// expected result and the actual result with both a human-readable
// string representation of the error, if available, as well as the
// hex result code.
# define EXPECT_HRESULT_SUCCEEDED(expr) \
EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr))
# define ASSERT_HRESULT_SUCCEEDED(expr) \
ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr))
# define EXPECT_HRESULT_FAILED(expr) \
EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr))
# define ASSERT_HRESULT_FAILED(expr) \
ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr))
#endif // GTEST_OS_WINDOWS
如何在实际项目中测试COM,可能还要继续深入了解一下。
COM在这里用于与.NET进行互操作,通过COM Interop(类型库导入工具tlbimp.exe,将COM中的*.idl或者*.exe导入到.NET DLL元数据)生成了互操作程序集(dll),可以将对COM的测试,转到用Nunit进行测试,由于本文档主要是针对Google Test的,关于如何在.NET中测试COM生成的互操作程序集,不在此进行说明 ,具体测试方法我已经在ATL_COM项目上进行了说明,在此只给出成功和失败的界面:
5. Parameterized
First,有两个问题:
a. 为什么要参数化?
b. 如何参数?
第一个问题,我们为什么要进行参数化测试,如果我们写了一个函数(根据不同的参数返回不同的值),我们要测试该函数是否正确,我们可能要使用不同的参数 ,以gtest sample文件夹中的sample1.cc里面的 bool IsPrime(int n)为例,我们可能要使用多种参数对其进行测试。当然我们可以写多次EXPECT_*,但基本上只是参数上的不同,参数化测试可以进行相应的简化工作。
第二个问题,我们如何要进行参数化测试,其实很简单,分为如下几步:
a).First,你要定义一个类,必须让该类继承::testing::WithParamInterface<T>,其中T就是你要进行参数化的类型:
// call GetParam() from TestWithParam<T> to get the test parameter.
class IsPrimeParamTest : public::testing::TestWithParam<int>
{
// You can implement all the usual class fixture members here.
};
b). Then,使用TEST_P宏去定义Test Case (与上面的TEST宏不同,这里是TEST_P,其中P是parameterized/ pattern的意思),在这个TEST_P里面,我们可以使用TestWithParam<T>类中的GetParam()方法去取得相应的测试参数(那么测试参数从哪获取呢,不急,看第三步):
TEST_P(IsPrimeParamTest, HandleTrueReturn)
{
// Inside a test, access the test parameter with the Getparam() method
// of the TestWithParam<T> class:
int n = GetParam();
// Virify : condition is true
EXPECT_TRUE(IsPrime(n));
}
c).Finally,使用INSTANTIATE_TEST_CASE_P,实例化(Instantiate)一个测试案例:
//Instantiate test case
INSTANTIATE_TEST_CASE_P(TrueReturn, IsPrimeParamTest, testing::Values(3, 5, 11, 23, 17));
本例中其中第三个参数,使用了testing命名空间中的Values。
在testing命名空间中多种函数去生成相应的测试参数,我们可以使用以下任一函数:
|
Range(begin, end[, step]) |
Yields values {begin, begin+step, begin+step+step, ...}. The values do not include end. step defaults to 1. |
|
Values(v1, v2, ..., vN) |
Yields values {v1, v2, ..., vN}. |
|
ValuesIn(container) and ValuesIn(begin, end) |
Yields values from a C-style array, an STL-style container, or an iterator range [begin, end). container, begin, and end can be expressions whose values are determined at run time. |
|
Bool() |
Yields sequence {false, true}. |
|
Combine(g1, g2, ..., gN) |
Yields all combinations (the Cartesian product for the math savvy) of the values generated by the N generators. This is only available if your system provides the <tr1/tuple> header. If you are sure your system does, and Google Test disagrees, you can override it by defining GTEST_HAS_TR1_TUPLE=1. See comments in include/gtest/internal/gtest-port.h for more information. |
该Demo生成测试结果如下:
6. Global Set-Up and Tear-Down
SetUp和TearDown可以放在多处,可以将其放在Test level,Test Case level,Test Program level,这里我们将其作为全局的,放在应用程序级别,在所有案例的执行前后。
首先看下所要继承的Environment 类
class Environment
{
public:
virtual ~Environment() {}
// Override this to define how to set up the environment.
virtual void SetUp() {}
// Override this to define how to tear down the environment.
virtual void TearDown() {}
};
我们要自己编写相应的子类:
class MyEnvironment : public testing::Environment
{
public:
virtual void SetUp()
{
cout << "Environment SetUp...,\n
you can set samething how to set up the environment" << endl;
}
virtual void TearDown()
{
cout << "Environment TearDown...,\n
you can set samething how to tear down the environment" << endl;
}
};
接着使用::testing::AddGlobalTestEnvironment()在main实例化我们的子类:
testing::Environment* const my_env =
testing::AddGlobalTestEnvironment(new MyEnvironment);则在所有的案例执行前和执行后会分别启动相应的SetUp,TearDown,如图所示:
7. Other
其它还有比较杂乱和高级的主题,如FLAGS_gtest_*之类的方法很多,在此只好列出Demo中用到的进行说明,完整的请查看Google文档:
- FLAGS_gtest_output
testing::FLAGS_gtest_output="xml:";
则生成类似的XML:
)。
- FLAGS_gtest_death_test_style
::testing::FLAGS_gtest_death_test_style = "fast";
::testing::FLAGS_gtest_death_test_style = "threadsafe";
(用于死亡测试,死亡测试DeathTest的优先级比普通Test的要高,详情见Part2)
- FLAGS_gtest_also_run_disabled_tests
可以对禁用的断言进行测试 。设置如下:
其它如上面提到的死亡测试(DeathTest)、禁用断言(DISABLED_)、事件(Event Listeners)等 ,见附件上的Demo或者Part 2部分。
8. Google C++ Testing Framework Samples
从http://code.google.com/p/googletest/wiki/V1_6_Samples可以下载到Google提供的Samples,这 里贴出其中的Sample #1,进行分析:
Sample#1由以下几个部分组成:
在 sample1.cc有如下两个方法:
// Returns n! (the factorial of n). For negative n, n! is defined to be 1.
int Factorial(int n) { ... }
// Returns true iff n is a prime number.
bool IsPrime(int n) { ... }
在sample1_unittest.cc中就是对这两个方法的测试,可以看出具体分为三步:
// Step 1. Include necessary header files such that the stuff your
// test logic needs is declared.
//
// Don't forget gtest.h, which declares the testing framework.
#include "stdafx.h"
#include <limits.h>
#include "sample1.h"
#include "gtest/gtest.h"
// Step 2. Use the TEST macro to define your tests.
//
// TEST has two parameters: the test case name and the test name.
// After using the macro, you should define your test logic between a
// pair of braces. You can use a bunch of macros to indicate the
// success or failure of a test. EXPECT_TRUE and EXPECT_EQ are
// examples of such macros. For a complete list, see gtest.h.
//
// <TechnicalDetails>
//
// In Google Test, tests are grouped into test cases. This is how we
// keep test code organized. You should put logically related tests
// into the same test case.
//
// The test case name and the test name should both be valid C++
// identifiers. And you should not use underscore (_) in the names.
//
// Google Test guarantees that each test you define is run exactly
// once, but it makes no guarantee on the order the tests are
// executed. Therefore, you should write your tests in such a way
// that their results don't depend on their order.
//
// </TechnicalDetails>
// Tests Factorial().
// Tests factorial of negative numbers.
TEST(FactorialTest, Negative) {
// This test is named "Negative", and belongs to the "FactorialTest"
// test case.
EXPECT_EQ(1, Factorial(-5));
EXPECT_EQ(1, Factorial(-1));
EXPECT_TRUE(Factorial(-10) > 0);
// <TechnicalDetails>
//
// EXPECT_EQ(expected, actual) is the same as
//
// EXPECT_TRUE((expected) == (actual))
//
// except that it will print both the expected value and the actual
// value when the assertion fails. This is very helpful for
// debugging. Therefore in this case EXPECT_EQ is preferred.
//
// On the other hand, EXPECT_TRUE accepts any Boolean expression,
// and is thus more general.
//
// </TechnicalDetails>
}
// Tests factorial of 0.
TEST(FactorialTest, Zero) {
EXPECT_EQ(1, Factorial(0));
}
// Tests factorial of positive numbers.
TEST(FactorialTest, Positive) {
EXPECT_EQ(1, Factorial(1));
EXPECT_EQ(2, Factorial(2));
EXPECT_EQ(6, Factorial(3));
EXPECT_EQ(40320, Factorial(8));
}
// Tests IsPrime()
// Tests negative input.
TEST(IsPrimeTest, Negative) {
// This test belongs to the IsPrimeTest test case.
EXPECT_FALSE(IsPrime(-1));
EXPECT_FALSE(IsPrime(-2));
EXPECT_FALSE(IsPrime(INT_MIN));
}
// Tests some trivial cases.
TEST(IsPrimeTest, Trivial) {
EXPECT_FALSE(IsPrime(0));
EXPECT_FALSE(IsPrime(1));
EXPECT_TRUE(IsPrime(2));
EXPECT_TRUE(IsPrime(3));
}
// Tests positive input.
TEST(IsPrimeTest, Positive) {
EXPECT_FALSE(IsPrime(4));
EXPECT_TRUE(IsPrime(5));
EXPECT_FALSE(IsPrime(6));
EXPECT_TRUE(IsPrime(23));
}
// Step 3. Call RUN_ALL_TESTS() in main().
//
// We do this by linking in src/gtest_main.cc file, which consists of
// a main() function which calls RUN_ALL_TESTS() for us.
//
// This runs all the tests you've defined, prints the result, and
// returns 0 if successful, or 1 otherwise.
//
// Did you notice that we didn't register the tests? The
// RUN_ALL_TESTS() macro magically knows about all the tests we
// defined. Isn't this convenient?
另外对于第三步Google提供的Samples都是直接调用
return RUN_ALL_TESTS();
对于要启动新的实例,最终的结果都是一闪而过,如果不是关心其具体的返回值,可以改成如下形式:
cin.get();
return 0;
这样就可以了。
最后的测试全部通过:
9. UI
另外类似于Nunit所提供的两个测试方式 (nunit-console.exe 、 nunit-gui.exe),Google Test也提供了一个UI的工具,经过分析源码,知道所有的代码测试最终会生成 一个.exe,如附件上的Demo,在Debug下面的glibtest.exe:
可以从命令行进行测试 ,且可以带入相应的运行参数(通过main的 int argc, _TCHAR* argv[] 带入到testing::InitGoogleTest(&argc, argv);),UI(gtest-gbar)就是使用这个来进行测试的(这个UI同Nunit UI一样,也是用C# /Windows Form开发的,个人感觉,或许它们新的版本都会升级使用C#/WPF进行UI开发)。它的UI如下:
它的C#代码中,最关键的就是用了一个继承自Component 的 System.Diagnostics.Process类,这个类可以启动相应的进程(如打开IE等):
这么强大的工具在哪可以下呢?请点击:http://code.google.com/p/gtest-gbar/
The Wall!
最近搬家,书太多,搞出了满满二大包带一箱书,主要有JavaScript,jQuery,ExtJS,C#,LINQ,AJAX,Silverlight,.NET,SQLServer,C++,JAVA等,因为搬家麻烦,想先卖出几本,有要的朋友到淘宝上购买:
http://item.taobao.com/auction/item_detail.htm?item_num_id=8342968882 -- ASP.NET AJAX实战
http://item.taobao.com/auction/item_detail.htm?item_num_id=8343084652 -- C#本质论(第3版)
http://item.taobao.com/auction/item_detail.htm?item_num_id=8342860582 -- ASP.NET电子商务开发实战
http://item.taobao.com/auction/item_detail.htm?item_num_id=8342729634 -- Microsoft SQL Server2005技术内幕:T-SQL程序设计
http://item.taobao.com/auction/item_detail.htm?item_num_id=8342553090 -- Silverlight 揭秘
如果这些都卖了的话,我再放几个卖卖,例外价格可商量!
===the wall===
目前除了使用Eclipse进行Andriod的开发外,Moto公司还进行了扩展使得可以更好地Moto手机的开发,该工具有如下特点:
完整开发包 面向摩托罗拉手机 代码段 应用程序创建向导 数据库管理 手机模拟器 配置包 使用 C 开发
具体详情如下图如示(如下图片摘自Moto公司):

下载地址: http://developer.motorola.com/docstools/motodevstudio/download/
提供两种模式:
- FullInstaller: Includes Eclipse environment

- EclipsePlug-in Version: Requires an existing Eclipse environment

一些相应的信息,诸如2.0 版本中的新增功能,已知问题等可查看
http://developer.motorola.com/docstools/library/MOTODEV_Studio_for_Android_Release_Notes/
欢迎界面如下:

好了,就说这么多!
,
本来准备在amazon上买一本,但加上空运等费用大约200-300人民币,已经下订单的我,想了想第二天取消了该订单,自己请别人打印了一本,还不错,
看英文的比中文的爽多了。
这本书非常不错。可惜3.0的翻译太差,如果有想法,可以一起咱们重新翻译一下。
这本书不错,看完这本书,对C#的理解会有一定的提高 ,要知道Scott Guthrie也看这本书!
话不多说,多说无益!

