12
返回列表 发新帖
楼主: AlexQin

[转载] 面向 C++ 的测试驱动开发

[复制链接]
论坛徽章:
1056
紫蜘蛛
日期:2015-09-22 15:53:22紫蜘蛛
日期:2015-10-15 13:48:52紫蜘蛛
日期:2015-10-15 14:45:48紫蜘蛛
日期:2015-10-15 14:47:47紫蜘蛛
日期:2015-10-15 14:48:45九尾狐狸
日期:2015-09-22 15:53:22九尾狐狸
日期:2015-10-15 13:50:37九尾狐狸
日期:2015-10-15 14:45:48九尾狐狸
日期:2015-10-15 14:47:47九尾狐狸
日期:2015-10-15 14:48:45
11#
 楼主| 发表于 2016-8-10 10:16 | 只看该作者
5. 我要能判断是不是棋盘已满并无赢家。
这个需求用于判断是否是和棋的情况,棋盘满了但并无赢家,这是可能出现的一种情况,这个实现设计可以有两种方式. 一是重构 CheckWinOut 函数,使返回值携带更多的信息,比如和棋,有人胜出等。二是定义一个独立的函数去判断棋盘的当前状态。第一种方案较合理,开始设计这种方案的测试用例:
  1. <font face="微软雅黑" size="3">EST_F(TicTacToeTestFixture,IsEndedInADraw)
  2. {
  3. char xChess='X',yChess='O';
  4. IGameBoard *gameBoard=new SimpleGameBoard("simpleBoard");
  5. gameBoard->PutChess(0,0,yChess);gameBoard->PutChess(0,1,xChess);gameBoard->PutChess(0,2,yChess);
  6. gameBoard->PutChess(1,0,xChess);gameBoard->PutChess(1,1,yChess);gameBoard->PutChess(1,2,yChess);
  7. gameBoard->PutChess(2,0,xChess);gameBoard->PutChess(2,1,yChess);gameBoard->PutChess(2,2,xChess);

  8. GameBoardStatus status=gameBoard->CheckWinOut(yChess);
  9. EXPECT_TRUE(status==GAMEDRAW);
  10. GameBoardStatus status2=gameBoard->CheckWinOut(xChess); EXPECT_TRUE(status2==GAMEDRAW);
  11. delete gameBoard;
  12. }</font>
复制代码

以上的测试用例可以看出, 我设计了和棋的棋局,并想重构 CheckWinout 函数,使其返回枚举类型 GameBoardStatus 以表示棋局的状态,其中 GAMEDRAW 表示和棋状态。为了使工程能编译通过,开始定义这个枚举类型并重构 CheckWinOut 函数。实现所有设计,经过几次的 Red 失败,最终 形成代码:
  1. <font face="微软雅黑" size="3">GameBoardStatus SimpleGameBoard::CheckWinOut(char chess)
  2. {

  3. if(IsThreeInLine_(chess)){
  4. return GAMEMWINOUT;
  5.         }
  6. else if(IsEndedInADraw_()){
  7. return GAMEDRAW;
  8.         }
  9. else{
  10. return GAMERUNNING;
  11.         }
  12. }</font>
复制代码

其中那个 IsEndedInADraw_是个受保护的成员函数,用于检测是否和棋。 在调通这个测试用例的过程中,我也更新了测试JugeThreeInLine。因为重构 ChecWinOut 改变了返回类型。

使用道具 举报

回复
论坛徽章:
1056
紫蜘蛛
日期:2015-09-22 15:53:22紫蜘蛛
日期:2015-10-15 13:48:52紫蜘蛛
日期:2015-10-15 14:45:48紫蜘蛛
日期:2015-10-15 14:47:47紫蜘蛛
日期:2015-10-15 14:48:45九尾狐狸
日期:2015-09-22 15:53:22九尾狐狸
日期:2015-10-15 13:50:37九尾狐狸
日期:2015-10-15 14:45:48九尾狐狸
日期:2015-10-15 14:47:47九尾狐狸
日期:2015-10-15 14:48:45
12#
 楼主| 发表于 2016-8-10 10:19 | 只看该作者
6. 我需要能复位棋盘,以便于重新开始下棋。

7. 我需要用对记住玩家,以便于我能特例化 Player。
6 和 7 需求的测试案例和实现比较比较简单,不在赘述,7 的要求是要建立玩家 Player,这个主要是说要能实例化玩家。可以看附带的工程。

8. 我需要能保存和加载棋局能力,以便于我能下次回来继续之前的游戏
这个需求是一个合理的需求,玩家可以保存和继续回来玩游戏,他的测试用例可以这样设计:
  1. <font face="微软雅黑" size="3">TEST_F(TicTacToeTestFixture,SaveTheBoard)
  2. {
  3. IGameBoard * gameBoard=new SimpleGameBoard("simpleBoard");
  4. char xChess='x',yChess='o';
  5. gameBoard->PutChess(0,0,xChess);
  6. gameBoard->PutChess(1,2,yChess);
  7. IGameIO *gameIO=new SimpleGameIO();
  8. EXPECT_NO_THROW(gameIO->save(gameBoard,"somewhere"));
  9. delete gameBoard;
  10. delete gameIO;
  11. }
  12. TEST_F(TicTacToeTestFixture,LoadTheBoard)
  13. {
  14. IGameBoard * gameBoard=new SimpleGameBoard("simpleBoard");
  15. char xChess='x',yChess='o';
  16. gameBoard->PutChess(0,0,xChess);
  17. gameBoard->PutChess(1,2,yChess);
  18. IGameIO *gameIO=new SimpleGameIO();
  19. EXPECT_NO_THROW(gameIO->save(gameBoard,"somewhere"));
  20. IGameBoard *game=gameIO->load("somewhere");
  21. EXPECT_EQ(xChess,game->GetChess(0,0));
  22. EXPECT_EQ(yChess,game->GetChess(1,2));
  23. EXPECT_EQ('+',game->GetChess(2,2));
  24. delete game;
  25. delete gameBoard;
  26. delete gameIO;
  27. }</font>
复制代码

这里用两个测试用例来覆盖这个需求,一个是保存棋盘,一个是加载棋盘。由这个测试用例可以看到,要通过这个测试,必须要定义 IGameIO 接口和 SimpeGameIO 类。 保存棋盘的媒介是文件。按照 TDD 的开发要求,测试单元本身最好是脱离对第三方系统的依赖,但测试中必然会用到第三方系统,解决这些问题的方法有几种。创建第三方系统的 Stub 类或是 FakedObject,第三种选择是 Mock 框架,如 Gmock。 Gmock 的设计理念是基于接口的,只要是第三方访问提供的是接口,这些访问就可以可以被用 Gmock 模拟。可以看参考文献获取更多的信息。 限于篇幅不再赘述。一下是完成所有测试用例的测试结果。
图 7.测试用例输出
或许你会注意到有些测试用例的设计,只是以点盖面,如果想要更多的验证点可以借助于 Gtest 提供的参数化测试设计测试数据,然后去测试实现的类和逻辑。 还有死亡测试的用例,可以在参考资源中的 Gtest 资源中查看。




使用道具 举报

回复
论坛徽章:
1056
紫蜘蛛
日期:2015-09-22 15:53:22紫蜘蛛
日期:2015-10-15 13:48:52紫蜘蛛
日期:2015-10-15 14:45:48紫蜘蛛
日期:2015-10-15 14:47:47紫蜘蛛
日期:2015-10-15 14:48:45九尾狐狸
日期:2015-09-22 15:53:22九尾狐狸
日期:2015-10-15 13:50:37九尾狐狸
日期:2015-10-15 14:45:48九尾狐狸
日期:2015-10-15 14:47:47九尾狐狸
日期:2015-10-15 14:48:45
13#
 楼主| 发表于 2016-8-10 10:20 | 只看该作者
结论
C++中实现测试驱动开发 TDD 之前是很困难的事。 但有了类似于 xUnit 的 Gtest 和 Gmock 测试框架,在 C++工程中实现 TDD 也变得很享受。测试驱动开发是一个很好的工具,它可以帮助开发者实现有机开发,在需求的实现过程中快速得到反馈,另一个好处是测试驱动开发可以使开发人员更加重视需求和测试,以测试用例为中心,这样势必会产生更好代码。从软件工程的角度来说,测试驱动开发的实践应用会大幅度的提高软件开发的质量,用代码级别的测试用例来覆盖和保障程序的健壮性更能保障整个软件产品的开发质量。

测试驱动开发的座右铭模式:红色-绿色-重构,然后重复这个直到开发完成为止,是一个自我确认和有保护代码重构的过程。采用测试驱动开发的模式的软件产品,产生的单元测试代码,从代码级别测试覆盖了软件的需求,使以后的代码重构更安全可靠。

使用道具 举报

回复
论坛徽章:
1056
紫蜘蛛
日期:2015-09-22 15:53:22紫蜘蛛
日期:2015-10-15 13:48:52紫蜘蛛
日期:2015-10-15 14:45:48紫蜘蛛
日期:2015-10-15 14:47:47紫蜘蛛
日期:2015-10-15 14:48:45九尾狐狸
日期:2015-09-22 15:53:22九尾狐狸
日期:2015-10-15 13:50:37九尾狐狸
日期:2015-10-15 14:45:48九尾狐狸
日期:2015-10-15 14:47:47九尾狐狸
日期:2015-10-15 14:48:45
14#
 楼主| 发表于 2016-8-10 10:21 | 只看该作者
参考资料

学习
讨论
  • 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。



使用道具 举报

回复
论坛徽章:
1056
紫蜘蛛
日期:2015-09-22 15:53:22紫蜘蛛
日期:2015-10-15 13:48:52紫蜘蛛
日期:2015-10-15 14:45:48紫蜘蛛
日期:2015-10-15 14:47:47紫蜘蛛
日期:2015-10-15 14:48:45九尾狐狸
日期:2015-09-22 15:53:22九尾狐狸
日期:2015-10-15 13:50:37九尾狐狸
日期:2015-10-15 14:45:48九尾狐狸
日期:2015-10-15 14:47:47九尾狐狸
日期:2015-10-15 14:48:45
15#
 楼主| 发表于 2016-8-10 10:21 | 只看该作者

使用道具 举报

回复
论坛徽章:
1056
紫蜘蛛
日期:2015-09-22 15:53:22紫蜘蛛
日期:2015-10-15 13:48:52紫蜘蛛
日期:2015-10-15 14:45:48紫蜘蛛
日期:2015-10-15 14:47:47紫蜘蛛
日期:2015-10-15 14:48:45九尾狐狸
日期:2015-09-22 15:53:22九尾狐狸
日期:2015-10-15 13:50:37九尾狐狸
日期:2015-10-15 14:45:48九尾狐狸
日期:2015-10-15 14:47:47九尾狐狸
日期:2015-10-15 14:48:45
16#
 楼主| 发表于 2016-8-13 15:18 | 只看该作者
good job

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表