复杂的东西写多了,如今写点简单的好了。由于功能上的需要, Vczh Library++3.0 被我搞得很离谱。为了开发维护的遍历、减少粗心犯下的错误以及增强单元测试、回归测试和测试工具,因此记录下一些开发上的小技巧,以便抛砖引玉,造福他人。欢迎高手来喷,菜鸟膜拜。

之前的文章 讲了指针和内存的一些问题,今天说一下单元测试的问题。如果在团队里面没有对单元测试的框架有要求的话,其实我们可以使用一个最简单的方法来搭建在IDE里面运行的单元测试框架,整个框架只需十几行代码。我们先来考虑一下功能最少的单元测试框架需要完成什么样的内容。首先我们要运行一个一个的测试用例,其次在一个测试用例里面我们要检查一些条件是否成立。举个例子,我们写一个函数将两个字符串连接起来,一般来说要进行下面的测试:

 1 #include "MyUnitTestFramework.h"//等一下我们会展示一下如何用最少的代码完成这个头文件的内容
 2 #include ""
 3 
 4 TEST_CASE(StringConcat)
 5 {
 6   TEST_ASSERT(concat("a", "b")=="ab");
 7   TEST_ASSERT(concat("a", "")=="a");
 8   TEST_ASSERT(concat("", "b")=="b");
 9   TEST_ASSERT(concat("", "")=="");
10   .
11 }
12 
13 int wmain()
14 {
15   return 0;
16 }

如果我们的单元测试框架可以这么写,那显然做起什么事情来都会方便很多,而且不需要向一些其他的测试框架一样注册一大堆东西,或者是写一大堆配置函数。当然这次我们只做功能最少的测试框架,这个框架除了运行测试以外,不会有其他功能,譬如选择哪些测试可以运行啦,还是在出错的时候log一些什么啦之类。之所以要在IDE里面运行,是因为我们如果做到TEST_ASSERT中出现false的话,立刻在该行崩溃,那么IDE就会帮你定位到出错的TEST_ASSERT中去,然后给你显示所有的上下文信息,譬如说callstack啦什么的。友好的工具不用简直对不起自己啊,干吗非得把单元测试做得那么复杂捏,凡是单元测试,总是要全部运行通过才能提交代码的。

那么我们来看看上面的单元测试的代码。首先写了TEST_CASE的那个地方,大括号里面的代码会自动运行。其次TEST_ASSERT会在表达式是false的时候崩溃。先从简单的入手吧。如何制造崩溃呢?最简单的办法就是抛异常:

1 #define TEST_ASSERT(e) do(if(!(e))throw "今晚没饭吃。";}while(0)

这里面有两个要注意的地方。首先e要加上小括号,不然取反操作符就有可能做出错误的行为。譬如说当e是a+b==c的时候,加了小括号就变成if(!(a+b==c))...,没有加小括号就变成if(!a+b==c)...,意思就完全变了。第二个主意的地方是我使用do{...}while(0)把语句包围起来了。这样做的好处是可以在任何时候TEST_ASSERT(e)都像一个语句。譬如我们可能这么写:

1 if(a)
2   TEST_ASSERT(x1);
3 else if(b)
4 {
5   TEST_ASSERT(x2);
6   TEST_ASSERT(x3);
7 }

如果没有do{...}while(0)包围起来,这个else就会被绑定到宏里面的那个if,你的代码就被偷偷改掉了。

那么现在剩下TEST_CASE(x){y}了。什么东西可以在main函数外面自动运行呢?这个我想熟悉C++的人都会知道,就是全局变量的构造函数啦。所以TEST_CASE(x){y}那个大括号里面的y只能在全局变量的构造函数里面调用。但是我们知道写一个类的时候,构造函数的大括号写完了,后面还有类的大括号,全局变量的名称,和最终的一个分号。为了把这些去掉,那么显然{y}应该属于一个普通的函数。那么全局变量如何能够使用这个函数呢?方法很简单,把函数前置声明一下就行了:

 1 #define TEST_CASE(NAME)                                            \
 2         extern void TESTCASE_##NAME();                             \
 3         namespace vl_unittest_executors                            \
 4         {                                                          \
 5             class TESTCASE_RUNNER_##NAME                           \
 6             {                                                      \
 7             public:                                                \
 8                 TESTCASE_RUNNER_##NAME()                           \
 9                 {                                                  \
10                     TESTCASE_##NAME();                             \
11                 }                                                  \
12             } TESTCASE_RUNNER_##NAME##_INSTANCE;                   \
13         }                                                          \
14         void TESTCASE_##NAME()

那我们来看看TEST_CASE(x){y}究竟会被翻译成什么代码:

 1 extern void TESTCASE_x();
 2 namespace vl_unittest_executors
 3 {
 4     class TESTCASE_RUNNER_x
 5     {
 6     public:
 7         TESTCASE_RUNNER_x()
 8         {
 9             TESTCASE_x();
10         }
11     } TESTCASE_RUNNER_x_INSTANCE;
12 }
13 void TESTCASE_x(){y}

到了这里是不是很清楚了捏,首先在main函数运行之前TESTCASE_RUNNER_x_INSTANCE变量会初始化,然后调用TESTCASE_RUNNER_x的构造函数,最后运行函数TESTCASE_x,该函数的内容显然就是{y}了。这里还能学到宏是如何连接两个名字成为一个名字,和如何写多行的宏的。

于是MyUnittestFramework.h就包含这两个宏,其他啥都没有,是不是很方便呢?打开Visual C++,建立一个工程,引用这个头文件,然后写你的单元测试,最后F5就运行了,多方便啊,啊哈哈哈。

这里需要注意一点,那些单元测试的顺序是不受到保证的,特别是你使用了多个cpp文件的情况下。于是你在使用这个测试框架的同时,会被迫保证执行一次单元测试不会对你的全局状态带来什么副作用,以便两个测试用例交换顺序执行的时候仍然能稳定地产生相同的结果。这对你写单元测试有帮助,而且为了让你的代码能够被这么测试,你的代码也会写的有条理,不会依赖全局状态,真是一举两得也。而且说不定单元测试用例比你的全局变量的初始化还先执行呢,因此为了使用这个测试框架,你将会不得不把你的全局变量隐藏在一个cpp里面,而暴露出随时可以被调用的一组函数出来。这样也可以让你的代码在使用全局状态的时候更加安全。

今天就讲到这里了。下一篇要写什么我还没想好,到时候再说吧。

from: http://www.cppblog.com/vczh/archive/2010/06/27/118829.html

C++实用技巧(四)相关推荐

  1. 实用技巧四:FScapture--滑动截图工具

    在学习工作中,你是否遇到过这样的需求:给领导汇报一个表单设计图,这个图又分为多个页面.怎么解决,微信一个一个的截图,这样既不美观又不方便. 它来喽--FastStoneCapture 十分好用的截图工 ...

  2. 麒麟子Cocos Creator实用技巧四:打包原生App截图白屏解决方案

    大家在做棋牌App或者一些特定需求的时候,需要截取当前游戏屏幕内容保存. 我们一般是采用cc.RenderTexture来截图并保存到游戏的可写目录 有时候会遇上,截出来的图片是白屏,或者部分白屏. ...

  3. python实用技巧(四)

    Python实用技巧(一) python实用技巧(二) python实用技巧(三) python实用技巧(四) 排序后,最后k个元素,以逆序输出 >>> l = range(1, 6 ...

  4. Windows2003实用技巧

    Windows2003实用技巧七则 一.跳过磁盘检修等待时间 一旦计算机因意外原因,例如突然停电或者死机的话,那么计算机下次重新启动的话,系统就会花10秒钟左右的时间,来运行磁盘扫描程序,检查磁盘是否 ...

  5. 数组反向遍历ios_iOS开发实用技巧—Objective-C中的各种遍历(迭代)方式

    iOS开发实用技巧-Objective-C中的各种遍历(迭代)方式 说明: 1)该文简短介绍在iOS开发中遍历字典.数组和集合的几种常见方式. 2)该文对应的代码可以在下面的地址获得:https:// ...

  6. Vim 实用技术,第 1 部分: 实用技巧

    0. Vim 简介 作为开源世界最重要的编辑器之一(另一个是 Emacs),Vim 以其强大的功能和可定制能力被众多开发者所喜爱.不过,也许就是因为 Vim 的功能太强大了,要真正用好 Vim 并不容 ...

  7. ppt格式刷快捷键_普通人 | 4个我在知乎上都没有见过的PPT实用技巧

    普通人栏目只教简单易操作.效果出众的技法.这是普通人栏目的第3期. 上期介绍了快捷键之后,我觉得读者反响还不错,所以这礼拜打算继续出一期普通人栏目. 本期介绍4个PPT制作实用技巧,最后一个 Offi ...

  8. cad在线转低版本_为什么别人制图那么快?41个CAD实用技巧,3天轻松玩转CAD

    获取更多业界资讯和深度好文● 点击蓝字关注我们 ● 点击上方蓝色字体 关注我们 我们在使用CAD制图时,总感觉自己的速度非常慢,为什么别人可以那么快的制图呢?今天就给大家分享一些CAD实用技巧,让你3 ...

  9. css 动态生成圆形区域内扇形个数_CSS实用技巧总结

    ❝ 作者:幻灵尔依 (授权原创) https://juejin.im/post/5e0fef935188253a624a6a72 ❞ <css揭秘>中讲了47个css技巧,其中有很多日常编 ...

  10. 邮件实用技巧九:如何快速查看历史邮件

    邮箱用久了,日积月累,各种各样的邮件充斥在整个邮箱空间中.对于商务人士而言,一个邮箱几千封邮件,已是稀松平常的事了.但怎么在几千封邮件里,翻查出自己需要的历史邮件呢,这不单止考验你的耐心,也考验你的技 ...

最新文章

  1. powershell创建iis站点、应用程序及应用程序池
  2. LibreOJ 6279 数列分块入门 3(分块+排序)
  3. 代码UITableView点击cell跳转
  4. 04-经典老歌(2000年后)
  5. 当你扛不住的时候就读读
  6. 中国大学MOOC 计算机组成原理第5章 测试(上)
  7. 截网页全屏图的方法-截网页全屏软件-Web2Pic Pro
  8. 【5折秒杀】戴尔轻薄商务本只卖2899元,狂降1000元
  9. selenium 处理cookie及switch的使用
  10. My interested stuff(2008-07-18)
  11. 昨天发生的四件大事!
  12. Java并发系列之并发编程的挑战
  13. 解决刷GApps时的Error 70报错或64
  14. 如何搭建DHCP服务器及自动获取IP地址及相关操作
  15. UiPath 网页元素识别
  16. 跟小甲鱼学Web开发笔记06(最新进展不按进度顺序)
  17. 最公正海贼王实力排行TOP50!
  18. L1-1 寻找250(10 分)
  19. 安装widows XP时蓝屏代码0X0000007B的问题的解决方案
  20. MySQL学习笔记——第3章 数据表的管理和表中数据操纵

热门文章

  1. 卷积神经网络的网络结构——以LeNet-5为例
  2. StatsD!次世代系统监控的核心
  3. Spring5 - 核心原理
  4. Spring-Spring整合MyBatis详解
  5. Java学习笔记(十一)--类与对象
  6. 学习笔记Spark(三)—— Spark架构及原理(spark架构、spark RDD)
  7. Qt 设置窗口背景图片的几种方法
  8. QPropertyAnimation实现游戏地图场景变换
  9. Ubuntu使用docker安装redmine
  10. java.lang.Math