如何用c++编一个电子生日贺卡贺卡程序?

C语言(55)
历时七天编写的UNO牌游戏。一般第一次使用一项技术来编写自己的程序(照着书上编的不算)的时候,我会忽视代码结构,以实现功能为主。因为第一次使用的技术往往没有经验,代码结构可能就因为无法实现功能而破坏了。所以第一次使用某项技术的时候我都是以实现功能为主。
总之这次的程序编写深刻认识到代码结构会决定以后一些功能的发展趋向,编写难度。想到什么就写什么,最终功能是实现了,但是后续的代码优化也麻烦很多了。都不知道在更低配置的电脑能不能运行流畅。
另外就是发现了以前编写链表的严重问题。给链表设定尾指针方便添加数据,但是在删除结点的时候要注意,如果删除了尾指针指向的结点则需要把尾指针挪回去。这点在单链表中不好实现,双向链表倒是可以往链表走。这个错误如果不是有大量的删除结点需求的话可能一直没发现。还要说明一点是Java的LinkedList链表是没有尾指针的,所以LInkedList添加数据到后期会比ArrayList慢很多,这点知道后也是汗颜,因为一般自己编写的链表都会加上尾指针,于是想当然的认为Java的链表也有尾指针了。
Card 卡片,用于分解卡片名字,获得卡片颜色和卡片功能,作比较用
CardDepthManager 用于管理打出的牌的显示顺序(深度)
ChangeColorW10 使用win10图标,在打出需要变色的牌的时候自行选择
GameSurfaceView 游戏界面视图,发牌,打牌,排列好牌等功能。
LinkedList 链表,用于存储四个人的牌号
Person 代表一个玩家,选择一张牌,删除牌,判断是否UNO等。
R 全局变量
spriteDeal 卡片处理,播放卡片移动动画,设置卡片大小,亮暗度
SpriteManager 导入110张UNO牌
UNODesktop.h
UNOAnimation UNO动画结构体,放入UNOAnimationManagement中读取数据并进行移动
UNOAnimationManagement 执行UNOAnimation
UNODesktop 将UNO桌面上打出的卡片放回后台发牌序列
DirArrow 右边的方向指示箭头,用于显示当前牌的方向
UNOButton 玩家的UNO按钮,按下后弹出UNOMsg UNO对话框
UNOMsg UNO对话框,没有出现而剩下一张牌的话会自动发两张牌
Number 每个人剩余牌的显示。
UNORules 用于执行UNO的各种规则,+4发四张牌,变色之类的
template.cpp 程序入口,整个程序(游戏引擎)的运行入口
操作方法:
1. 点击牌打出,亮牌表示可打。
2.. 右键摸牌。
3. 剩两张牌要打出一张牌的时候需要按UNO按钮。否则自动发两张牌。
version - 1.0 源代码与exe程序下载地址:http://download.csdn.net/detail/u92806
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:22880次
排名:千里之外
原创:77篇
评论:12条
(2)(6)(4)(4)(4)(24)(5)(11)(5)(3)(3)(1)(5)(4)(5)hdu 4550 卡片游戏
Time Limit:
MS (/Others)&&& Memory Limit:
K (Java/Others)
Total Submission(s): 92&&& Accepted Submission(s): 19
Problem Description
  小明最近宅在家里无聊,于是他发明了一种有趣的游戏,游戏道具是N张叠在一起的卡片,每张卡片上都有一个数字,数字的范围是0~9,游戏规则如下:
  首先取最上方的卡片放到桌子上,然后每次取最上方的卡片,放到桌子上已有卡片序列的最右边或者最左边。当N张卡片全部都放到桌子上后,桌子上的N张卡片构成了一个数。这个数不能有前导0,也就是说最左边的卡片上的数字不能是0。游戏的目标是使这个数最小。
  现在你的任务是帮小明写段程序,求出这个最小数。
第一行是一个数T,表示有T组测试数据;
然后下面有T行, 每行是一个只含有0~9的字符串,表示N张叠在一起的卡片,最左边的数字表示最上方的卡片。
[Technical Specification]
1 &= N &= 100
对于每组测试数据,请在一行内输出能得到的最小数。
Sample Input
Sample Output
2013金山西山居创意游戏程序挑战赛&&初赛(2)
尼妹的,不懂这题,我是看了别人的才会做的= =!
#include&iostream&&&
#include&cstdio&&&
#include&cstring&&&
#include&string&&&
#define N 1010&&
char s[N];&
int main(){&
&&& int T,i,j;&
&&& scanf(&%d&,&T);&
&&& while(T--){&
&&& scanf(&%s&,s);&
&&& a=s[0];&
&&& char m='9';&
&&& int j=0;&
&&& for(i=0;s[i];i++){&
&&&&&&& if(s[i]!='0'){//找到非0最小的数&&
&&&&&&&&&&& if(m&=s[i])m=s[j=i];&
&&&&&&& }&
&&&&&&& for(i=1;s[i];i++){&
&&&&&&&&&&& if(i==j)a=s[i]+a;&
&&&&&&&&&&& else if(i&j){&
&&&&&&&&&&&&&&& a+=s[i];&
&&&&&&&&&&& }else if(i&j){&
&&&&&&&&&&&&&&& if(s[i]&a[0])a+=s[i];&
&&&&&&&&&&&&&&& else a=s[i]+a;&
&&&&&&&&&&& }&
&&&&&&& }&
&&&&&&& cout&&a&&&
return 0;&
#include&iostream&
#include&cstdio&
#include&cstring&
#include&string&
#define N 1010
char s[N];
int main(){
&&& int T,i,j;
&&& scanf(&%d&,&T);
&&& while(T--){
&&& scanf(&%s&,s);
&&& a=s[0];
&char m='9';
&for(i=0;s[i];i++){
&&if(s[i]!='0'){//找到非0最小的数
&&&if(m&=s[i])m=s[j=i];
&&&&&&& for(i=1;s[i];i++){
&&&if(i==j)a=s[i]+a;
&&&else if(i&j){
&&&&a+=s[i];
&&&}else if(i&j){
&&&&if(s[i]&a[0])a+=s[i];
&&&&else a=s[i]+a;
&&&&&&& cout&&a&&
(window.slotbydup=window.slotbydup || []).push({
id: '2467140',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467141',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467143',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467148',
container: s,
size: '1000,90',
display: 'inlay-fix'从新手到高手:C++全方位学习-eNet网络学院
|| | | | |
| | |   |
  本教程作者:范磊,山东泰安科技大学教师,联系信箱:。擅长VC、MFC、DELPHI、J2SE开发手机游戏,VC编写数据库,VC游戏开发等。本教程为书籍配套教程,书籍名称:《零起点学通C++》
  基于这个理念,我完成了该书的创作,其中的艰辛自不必说,不过令人欣慰的是,该书确实能够行之有效地帮助后来人少走许多弯路。这是由于该书不会象其他书一样首先讲述理论知识,然后讲语言特征,在概括完语言特征后再去讲解语言的基本知识,结果往往使读者纠缠于编程底层的实现,而忽略了他应该掌握的内容。
  本书不再按照以往的模式,而是首先以一个最简短的C++程序讲起,通过对这个完整的程序的实际编写引申出一些相关的知识,然后在后面的教程中对该程序不断地扩大和完善,这样方才使读者不至于一上来就被C++吓坏,同时也能深刻地理解C++的各个特性的设置目的。
第一章 初识C++
第二章 从一个最简短的C++程序讲起
第三章 初步了解函数
第四章 C++数据类型
第五章 IF语句与逻辑运算符
第六章 面向对象面向对象程序设计
第七章 循环语句
第八章 指针
第九章 引用
第十章 深入函数
第十一章 运算符重载
第十二章 继承
第十三章 虚函数
第十四章 数组
第十五章 链表
第十六章 多态性
第十七章 特殊类成员
第十八章 字符串
第19章 代码重用
PS文章推荐
热门关键字:
网站合作、内容监督、商务咨询:010-
Copyright &
硅谷动力公司版权所有
未经授权禁止转载、摘编、复制或建立镜像.如有违反,追究法律责任.【声明】本网站部分内容属论坛网友发布,本网站仅提供网友交流平台,但有权在本网站范围内引用、发布、转载来自论坛网友发布的内容。网友发布内容纯属个人行为,与本网站立场无关。本网站对于论坛网友发布的内容所引发的版权、署名权的异议及纠纷,不承担任何责任。其他媒体转载须事先与原作者和本网站联系。
京ICP证000088号IBM Bluemix
点击按钮,开始云上的开发!
developerWorks 社区
googletest 与 googlemock 是 Google 公司于 2008 年发布的两套用于单元测试的应用框架,本文将向读者介绍如何应用这两套应用框架轻松编写 C++ 单元测试代码。以下讨论基于 gtest-1.2.1 及 gmock-1.0.0 。
(), 高级软件工程师, Adobe
熊伟(Wayne Xiong),华中科技大学硕士,曾用网名 Bill David、大卫、大笨熊等。精于 C++,后转入 JAVA 阵营,曾就职于 Lucent、BEA(Oracle)等公司,从事电信及 J2EE 应用平台的设计开发;现为 Adobe 公司高级软件工程师,主要从事 Flash Media Server 及 RIA 相关应用的设计开发。可以通过
或博客 http://blog.csdn.net/billdavid 与他联系。
单元测试概述测试并不只是测试工程师的责任,对于开发工程师,为了保证发布给测试环节的代码具有足够好的质量( Quality ),为所编写的功能代码编写适量的单元测试是十分必要的。单元测试( Unit Test ,模块测试)是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确,通过编写单元测试可以在编码阶段发现程序编码错误,甚至是程序设计错误。单元测试不但可以增加开发者对于所完成代码的自信,同时,好的单元测试用例往往可以在回归测试的过程中,很好地保证之前所发生的修改没有破坏已有的程序逻辑。因此,单元测试不但不会成为开发者的负担,反而可以在保证开发质量的情况下,加速迭代开发的过程。对于单元测试框架,目前最为大家所熟知的是 JUnit 及其针对各语言的衍生产品, C++ 语言所对应的 JUnit 系单元测试框架就是 CppUnit 。但是由于 CppUnit 的设计严格继承自 JUnit ,而没有充分考虑 C++ 与 Java 固有的差异(主要是由于 C++ 没有反射机制,而这是 JUnit 设计的基础),在 C++ 中使用 CppUnit 进行单元测试显得十分繁琐,这一定程度上制约了 CppUnit 的普及。笔者在这里要跟大家介绍的是一套由 google 发布的开源单元测试框架( Testing Framework ): googletest 。应用 googletest 编写单元测试代码googletest 是由 Google 公司发布,且遵循 New BSD License (可用作商业用途)的开源项目,并且 googletest 可以支持绝大多数大家所熟知的平台。与 CppUnit 不同的是: googletest 可以自动记录下所有定义好的测试,不需要用户通过列举来指明哪些测试需要运行。定义单元测试在应用 googletest 编写单元测试时,使用 TEST() 宏来声明测试函数。如:清单 1. 用 TEST() 宏声明测试函数TEST(GlobalConfigurationTest, configurationDataTest)
TEST(GlobalConfigurationTest, noConfigureFileTest)分别针对同一程序单元 GlobalConfiguration 声明了两个不同的测试(Test)函数,以分别对配置数据进行检查( configurationDataTest ),以及测试没有配置文件的特殊情况( noConfigureFileTest )。实现单元测试针对同一程序单元设计出不同的测试场景后(即划分出不同的 Test 后),开发者就可以编写单元测试分别实现这些测试场景了。在 googletest 中实现单元测试,可通过 ASSERT_* 和 EXPECT_* 断言来对程序运行结果进行检查。 ASSERT_* 版本的断言失败时会产生致命失败,并结束当前函数; EXPECT_* 版本的断言失败时产生非致命失败,但不会中止当前函数。因此, ASSERT_* 常常被用于后续测试逻辑强制依赖的处理结果的断言,如创建对象后检查指针是否为空,若为空,则后续对象方法调用会失败;而 EXPECT_* 则用于即使失败也不会影响后续测试逻辑的处理结果的断言,如某个方法返回结果的多个属性的检查。googletest 中定义了如下的断言:表 1: googletest 定义的断言( Assert )基本断言二进制比较字符串比较ASSERT_TRUE(condition);EXPECT_TRUE(condition);condition为真ASSERT_FALSE(condition);EXPECT_FALSE(condition);condition为假ASSERT_EQ(expected,actual);EXPECT_EQ(expected,actual);expected==actualASSERT_NE(val1,val2);EXPECT_NE(val1,val2);val1!=val2ASSERT_LT(val1,val2);EXPECT_LT(val1,val2);val1&val2ASSERT_LE(val1,val2);EXPECT_LE(val1,val2);val1&=val2ASSERT_GT(val1,val2);EXPECT_GT(val1,val2);val1&val2ASSERT_GE(val1,val2);EXPECT_GE(val1,val2);val1&=val2ASSERT_STREQ(expected_str,actual_str);EXPECT_STREQ(expected_str,actual_str);两个 C 字符串有相同的内容ASSERT_STRNE(str1,str2);EXPECT_STRNE(str1,str2);两个 C 字符串有不同的内容ASSERT_STRCASEEQ(expected_str,actual_str);EXPECT_STRCASEEQ(expected_str,actual_str);两个 C 字符串有相同的内容,忽略大小写ASSERT_STRCASENE(str1,str2);EXPECT_STRCASENE(str1,str2);两个 C 字符串有不同的内容,忽略大小写下面的实例演示了上面部分断言的使用:清单 2. 一个较完整的 googletest 单元测试实例// Configure.h
#pragma once
#include &string&
#include &vector&
class Configure
std::vector&std::string& vI
int addItem(std::string str);
std::string getItem(int index);
int getSize();
// Configure.cpp
#include "Configure.h"
#include &algorithm&
* @brief Add an item to configuration store. Duplicate item will be ignored
* @param str item to be stored
* @return the index of added configuration item
int Configure::addItem(std::string str)
std::vector&std::string&::const_iterator vi=std::find(vItems.begin(), vItems.end(), str);
if (vi != vItems.end())
return vi - vItems.begin();
vItems.push_back(str);
return vItems.size() - 1;
* @brief Return the configure item at specified index.
* If the index is out of range, "" will be returned
* @param index the index of item
* @return the item at specified index
std::string Configure::getItem(int index)
if (index &= vItems.size())
return "";
return vItems.at(index);
/// Retrieve the information about how many configuration items we have had
int Configure::getSize()
return vItems.size();
// ConfigureTest.cpp
#include &gtest/gtest.h&
#include "Configure.h"
TEST(ConfigureTest, addItem)
// do some initialization
Configure* pc = new Configure();
// validate the pointer is not null
ASSERT_TRUE(pc != NULL);
// call the method we want to test
pc-&addItem("A");
pc-&addItem("B");
pc-&addItem("A");
// validate the result after operation
EXPECT_EQ(pc-&getSize(), 2);
EXPECT_STREQ(pc-&getItem(0).c_str(), "A");
EXPECT_STREQ(pc-&getItem(1).c_str(), "B");
EXPECT_STREQ(pc-&getItem(10).c_str(), "");
}运行单元测试在实现完单元测试的测试逻辑后,可以通过 RUN_ALL_TESTS() 来运行它们,如果所有测试成功,该函数返回 0,否则会返回 1 。 RUN_ALL_TESTS() 会运行你链接到的所有测试――它们可以来自不同的测试案例,甚至是来自不同的文件。因此,运行 googletest 编写的单元测试的一种比较简单可行的方法是:为每一个被测试的 class 分别创建一个测试文件,并在该文件中编写针对这一 class 的单元测试;编写一个 Main.cpp 文件,并在其中包含以下代码,以运行所有单元测试:清单 3. 初始化 googletest 并运行所有测试#include &gtest/gtest.h&
int main(int argc, char** argv) {
testing::InitGoogleTest(&argc, argv);
// Runs all tests using Google Test.
return RUN_ALL_TESTS();
}最后,将所有测试代码及 Main.cpp 编译并链接到目标程序中。此外,在运行可执行目标程序时,可以使用 --gtest_filter 来指定要执行的测试用例,如:./foo_test 没有指定filter,运行所有测试;./foo_test --gtest_filter=* 指定filter为*,运行所有测试;./foo_test --gtest_filter=FooTest.* 运行测试用例FooTest的所有测试;./foo_test --gtest_filter=*Null*:*Constructor* 运行所有全名(即测试用例名 + “ . ” + 测试名,如 GlobalConfigurationTest.noConfigureFileTest)含有"Null"或"Constructor"的测试;./foo_test --gtest_filter=FooTest.*-FooTest.Bar 运行测试用例FooTest的所有测试,但不包括FooTest.Bar。这一特性在包含大量测试用例的项目中会十分有用。应用 googlemock 编写 Mock Objects很多 C++ 程序员对于 Mock Objects (模拟对象)可能比较陌生,模拟对象主要用于模拟整个应用程序的一部分。在单元测试用例编写过程中,常常需要编写模拟对象来隔离被测试单元的“下游”或“上游”程序逻辑或环境,从而达到对需要测试的部分进行隔离测试的目的。例如,要对一个使用数据库的对象进行单元测试,安装、配置、启动数据库、运行测试,然后再卸装数据库的方式,不但很麻烦,过于耗时,而且容易由于环境因素造成测试失败,达不到单元测试的目的。模仿对象提供了解决这一问题的方法:模仿对象符合实际对象的接口,但只包含用来“欺骗”测试对象并跟踪其行为的必要代码。因此,其实现往往比实际实现类简单很多。为了配合单元测试中对 Mocking Framework 的需要, Google 开发并于 2008 年底开放了: googlemock 。与 googletest 一样, googlemock 也是遵循 New BSD License (可用作商业用途)的开源项目,并且 googlemock 也可以支持绝大多数大家所熟知的平台。注 1:在 Windows 平台上编译 googlemock对于 Linux 平台开发者而言,编译 googlemock 可能不会遇到什么麻烦;但是对于 Windows 平台的开发者,由于 Visual Studio 还没有提供 tuple ( C++0x TR1 中新增的数据类型)的实现,编译 googlemock 需要为其指定一个 tuple 类型的实现。著名的开源 C++ 程序库 boost 已经提供了 tr1 的实现,因此,在 Windows 平台下可以使用 boost 来编译 googlemock 。为此,需要修改 %GMOCK_DIR%/msvc/gmock_config.vsprops ,设定其中 BoostDir 到 boost 所在的目录,如:&UserMacro
Name="BoostDir"
Value="$(BOOST_DIR)"
/&其中 BOOST_DIR 是一个环境变量,其值为 boost 库解压后所在目录。对于不希望在自己的开发环境上解包 boost 库的开发者,在 googlemock 的网站上还提供了一个从 boost 库中单独提取出来的 tr1 的实现,可将其下载后将解压目录下的 boost 目录拷贝到 %GMOCK_DIR% 下(这种情况下,请勿修改上面的配置项;建议对 boost 不甚了解的开发者采用后面这种方式)。在应用 googlemock 来编写 Mock 类辅助单元测试时,需要:编写一个 Mock Class (如 class MockTurtle ),派生自待 Mock 的抽象类(如 class Turtle );对于原抽象类中各待 Mock 的 virtual 方法,计算出其参数个数 n ;在 Mock Class 类中,使用 MOCK_METHODn() (对于 const 方法则需用 MOCK_CONST_METHODn() )宏来声明相应的 Mock 方法,其中第一个参数为待 Mock 方法的方法名,第二个参数为待 Mock 方法的类型。如下:清单 4. 使用 MOCK_METHODn 声明 Mock 方法#include &gmock/gmock.h&
// Brings in Google Mock.
class MockTurtle : public Turtle {
MOCK_METHOD0(PenUp, void());
MOCK_METHOD0(PenDown, void());
MOCK_METHOD1(Forward, void(int distance));
MOCK_METHOD1(Turn, void(int degrees));
MOCK_METHOD2(GoTo, void(int x, int y));
MOCK_CONST_METHOD0(GetX, int());
MOCK_CONST_METHOD0(GetY, int());
};在完成上述工作后,就可以开始编写相应的单元测试用例了。在编写单元测试时,可通过 ON_CALL 宏来指定 Mock 方法被调用时的行为,或 EXPECT_CALL 宏来指定 Mock 方法被调用的次数、被调用时需执行的操作等,并对执行结果进行检查。如下:清单 5. 使用 ON_CALL 及 EXPECT_CALL 宏using testing::R
// #1,必要的声明
TEST(BarTest, DoesThis) {
// #2,创建 Mock 对象
ON_CALL(foo, GetSize())
// #3,设定 Mock 对象默认的行为(可选)
.WillByDefault(Return(1));
// ... other default actions ...
EXPECT_CALL(foo, Describe(5))
// #4,设定期望对象被访问的方式及其响应
.WillRepeatedly(Return("Category 5"));
// ... other expectations ...
EXPECT_EQ("good", MyProductionFunction(&foo));
// #5,操作 Mock 对象并使用 googletest 提供的断言验证处理结果
// #6,当 Mock 对象被析构时, googlemock 会对结果进行验证以判断其行为是否与所有设定的预期一致其中, WillByDefault 用于指定 Mock 方法被调用时的默认行为; Return 用于指定方法被调用时的返回值; Times 用于指定方法被调用的次数; WillRepeatedly 用于指定方法被调用时重复的行为。对于未通过 EXPECT_CALL 声明而被调用的方法,或不满足 EXPECT_CALL 设定条件的 Mock 方法调用, googlemock 会输出警告信息。对于前一种情况下的警告信息,如果开发者并不关心这些信息,可以使用 Adapter 类模板 NiceMock 避免收到这一类警告信息。如下:清单 6. 使用 NiceMock 模板testing::NiceMock&MockFoo& nice_在笔者开发的应用中,被测试单元会通过初始化时传入的上层应用的接口指针,产生大量的处理成功或者失败的消息给上层应用,而开发者在编写单元测试时并不关心这些消息的内容,通过使用 NiceMock 可以避免为不关心的方法编写 Mock 代码(注意:这些方法仍需在 Mock 类中声明,否则 Mock 类会被当作 abstract class 而无法实例化)。与 googletest 一样,在编写完单元测试后,也需要编写一个如下的入口函数来执行所有的测试:清单 7. 初始化 googlemock 并运行所有测试#include &gtest/gtest.h&
#include &gmock/gmock.h&
int main(int argc, char** argv) {
testing::InitGoogleMock(&argc, argv);
// Runs all tests using Google Test.
return RUN_ALL_TESTS();
}下面的代码演示了如何使用 googlemock 来创建 Mock Objects 并设定其行为,从而达到对核心类 AccountService 的 transfer (转账)方法进行单元测试的目的。由于 AccountManager 类的具体实现涉及数据库等复杂的外部环境,不便直接使用,因此,在编写单元测试时,我们用 MockAccountManager 替换了具体的 AccountManager 实现。清单 8. 待测试的程序逻辑// Account.h
// basic application data class
#pragma once
#include &string&
class Account
std::string accountId;
Account();
Account(const std::string& accountId, long initialBalance);
void debit(long amount);
void credit(long amount);
long getBalance()
std::string getAccountId()
// Account.cpp
#include "Account.h"
Account::Account()
Account::Account(const std::string& accountId, long initialBalance)
this-&accountId = accountId;
this-&balance = initialB
void Account::debit(long amount)
this-&balance -=
void Account::credit(long amount)
this-&balance +=
long Account::getBalance() const
return this-&
std::string Account::getAccountId() const
return accountId;
// AccountManager.h
// the interface of external services which should be mocked
#pragma once
#include &string&
#include "Account.h"
class AccountManager
virtual Account findAccountForUser(const std::string& userId) = 0;
virtual void updateAccount(const Account& account) = 0;
// AccountService.h
// the class to be tested
#pragma once
#include &string&
#include "Account.h"
#include "AccountManager.h"
class AccountService
AccountManager* pAccountM
AccountService();
void setAccountManager(AccountManager* pManager);
void transfer(const std::string& senderId,
const std::string& beneficiaryId, long amount);
// AccountService.cpp
#include "AccountService.h"
AccountService::AccountService()
this-&pAccountManager = NULL;
void AccountService::setAccountManager(AccountManager* pManager)
this-&pAccountManager = pM
void AccountService::transfer(const std::string& senderId,
const std::string& beneficiaryId, long amount)
Account sender = this-&pAccountManager-&findAccountForUser(senderId);
Account beneficiary = this-&pAccountManager-&findAccountForUser(beneficiaryId);
sender.debit(amount);
beneficiary.credit(amount);
this-&pAccountManager-&updateAccount(sender);
this-&pAccountManager-&updateAccount(beneficiary);
}清单 9. 相应的单元测试// AccountServiceTest.cpp
// code to test AccountService
#include &map&
#include &string&
#include &gtest/gtest.h&
#include &gmock/gmock.h&
#include "../Account.h"
#include "../AccountService.h"
#include "../AccountManager.h"
// MockAccountManager, mock AccountManager with googlemock
class MockAccountManager : public AccountManager
MOCK_METHOD1(findAccountForUser, Account(const std::string&));
MOCK_METHOD1(updateAccount, void(const Account&));
// A facility class acts as an external DB
class AccountHelper
std::map&std::string, Account& mA
// an internal map to store all Accounts for test
AccountHelper(std::map&std::string, Account&& mAccount);
void updateAccount(const Account& account);
Account findAccountForUser(const std::string& userId);
AccountHelper::AccountHelper(std::map&std::string, Account&& mAccount)
this-&mAccount = mA
void AccountHelper::updateAccount(const Account& account)
this-&mAccount[account.getAccountId()] =
Account AccountHelper::findAccountForUser(const std::string& userId)
if (this-&mAccount.find(userId) != this-&mAccount.end())
return this-&mAccount[userId];
return Account();
// Test case to test AccountService
TEST(AccountServiceTest, transferTest)
std::map&std::string, Account& mA
mAccount["A"] = Account("A", 3000);
mAccount["B"] = Account("B", 2000);
AccountHelper helper(mAccount);
MockAccountManager* pManager = new MockAccountManager();
// specify the behavior of MockAccountManager
// always invoke AccountHelper::findAccountForUser
// when AccountManager::findAccountForUser is invoked
EXPECT_CALL(*pManager, findAccountForUser(testing::_)).WillRepeatedly(
testing::Invoke(&helper, &AccountHelper::findAccountForUser));
// always invoke AccountHelper::updateAccount
//when AccountManager::updateAccount is invoked
EXPECT_CALL(*pManager, updateAccount(testing::_)).WillRepeatedly(
testing::Invoke(&helper, &AccountHelper::updateAccount));
// inject the MockAccountManager object into AccountService
as.setAccountManager(pManager);
// operate AccountService
as.transfer("A", "B", 1005);
// check the balance of Account("A") and Account("B") to
//verify that AccountService has done the right job
EXPECT_EQ(1995, helper.findAccountForUser("A").getBalance());
EXPECT_EQ(3005, helper.findAccountForUser("B").getBalance());
// Main.cpp
#include &gtest/gtest.h&
#include &gmock/gmock.h&
int main(int argc, char** argv) {
testing::InitGoogleMock(&argc, argv);
// Runs all tests using Google Test.
return RUN_ALL_TESTS();
}注 2:上述范例工程详见附件。要编译该工程,请读者自行添加环境变量 GTEST_DIR 、 GMOCK_DIR ,分别指向 googletest 、 googlemock 解压后所在目录;对于 Windows 开发者,还需要将 %GMOCK_DIR%/msvc/gmock_config.vsprops 通过 View-&Property Manager 添加到工程中,并将 gmock.lib 拷贝到工程目录下。通过上面的实例可以看出, googlemock 为开发者设定 Mock 类行为,跟踪程序运行过程及结果,提供了丰富的支持。但与此同时,应用程序也应该尽量降低应用代码间的耦合度,使得单元测试可以很容易对被测试单元进行隔离(如上例中, AccountService 必须提供了相应的方法以支持 AccountManager 的替换)。关于如何通过应用设计模式来降低应用代码间的耦合度,从而编写出易于单元测试的代码,请参考本人的另一篇文章《》( developerWorks , 2008 年 7 月)。注 3:此外,开发者也可以直接通过继承被测试类,修改与外围环境相关的方法的实现,达到对其核心方法进行单元测试的目的。但由于这种方法直接改变了被测试类的行为,同时,对被测试类自身的结构有一些要求,因此,适用范围比较小,笔者也并不推荐采用这种原始的 Mock 方式来进行单元测试。总结Googletest 与 googlemock 的组合,很大程度上简化了开发者进行 C++ 应用程序单元测试的编码工作,使得单元测试对于 C++ 开发者也可以变得十分轻松;同时, googletest 及 googlemock 目前仍在不断改进中,相信随着其不断发展,这一 C++ 单元测试的全新组合将变得越来越成熟、越来越强大,也越来越易用。
下载描述名字大小完整的使用 googletest 及 googlemock 编写单元测试的例子4KB一个较完整的使用 googletest 编写单元测试的例子3KB
参考资料 “” ( developerWorks ,2003 年 3 月):介绍如何使用模仿对象替换合作者以改进单元测试。“” ( developerWorks ,2008 年 7 月):介绍如何应用设计模式编写易于单元测试的代码。“” :介绍如何应用 jMock , EasyMock 对单个的类进行隔离测试。“”( Martin Fowler ,2007 年 6 月):介绍 Mock Objects 概念及其与传统 Stubs 测试方式的区别。关于 googletest 的更多信息,请访问其项目主页:关于 googlemock 的更多信息,请访问其项目主页:
developerWorks: 登录
标有星(*)号的字段是必填字段。
保持登录。
单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件。
在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。
所有提交的信息确保安全。
选择您的昵称
当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。昵称长度在 3 至 31 个字符之间。
您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。
标有星(*)号的字段是必填字段。
(昵称长度在 3 至 31 个字符之间)
单击提交则表示您同意developerWorks 的条款和条件。 .
所有提交的信息确保安全。
文章、教程、演示,帮助您构建、部署和管理云应用。
立即加入来自 IBM 的专业 IT 社交网络。
为灾难恢复构建应用,赢取现金大奖。
static.content.url=/developerworks/js/artrating/SITE_ID=10Zone=LinuxArticleID=390832ArticleTitle=轻松编写 C++ 单元测试publish-date=

我要回帖

更多关于 电子生日贺卡 的文章

 

随机推荐