近来一段时间,我一直在迷恋Martin Fowler的Refactoring: Improving the Design of Existing Code(重构——改善既有代码的设计)这本书。不过在这里我不是要说重构,因为没看完呢,但在书中提到了一个技术是我想拿出来推荐给大家的,叫做“自我测试代码”(Self-testing Code)。
在我之前的项目中,还真没有真正用到过和亲自设计过自测程序,为了写这个博客,我又特地将我以前的一个项目进行了一次重构,当然结构的设计都已经满足需要了,说是重构,其实主要是想添加一些自测程序,用来实践这次所要说的内容,毕竟要想写点东西在网上自己没做过是不行的。借此机会边学习边总结也是件好的事情。以前我的项目也多少用到过测试,但是没有真正的代码自测。比如说我的解决方案下面可能有个页面专门来进行测试,但不是镶嵌在代码里面的。现在的测试程序是内嵌入代码的。
我先来简单说说在《重构》那本书中的junit测试框架,如图:
我们看到测试套件和测试案例都是依赖于Test接口的整个junit.framework结构并不复杂,就是程序员通过使用测试套件来进行代码测试,将测试结果通过FileReaderTester进行输出。
那么我来接着这个思路来设计一个.net下的自我测试结构(这里说“架构”有点太大了,用“结构”一词来吧)。
我打算通过讲解自我测试的概念和自我测试的输出两个方面来说明一下:
一、自我测试的概念
首先我来整理自我测试的概念,我个人认为自我测试分为两种类型:一种是实体数据的对比,比如实体数据结构是不是和规定范围内的时候数据结构一样;另一种是业务流程是不是在所要的流程范围内。总之自我测试是一种比较,比较测试在软件中的逻辑数据或者实体数据是不是在规定范围内。当然可能还会有别的类型的比较,这里我先不涉及到,有兴趣的朋友可以和我联系,我们共同交流和丰富“自我测试程序”的设计工作。
在这里我来举一个具体的实例来进行自我测试的讲解,之所以这样是因为我觉得自我测试这种东西要是通过进行详细理论上的说明大家太抽象了,那还不如从具体的实例出手,很多问题都是在具体项目当中,发挥自我想象力来编写测试代码的,我不想限定死测试的结构,也没这个必要去限定必须在什么地方测试。我只给大家一个原则,可能出错的地方就要有自我测试程序。什么地方容易出错呢?对,所有写代码的地方都可能出错。
我们来看这个例子:
这是一个最简单的根据用户权限进入Form窗体的例子,我想没有人不会吧。我们来给他加自测,首先我们在Program里面获取Use(这里是个简单的演示,先暂时不考虑系统架构的问题)。我们通过Use对象中的方法通过用户id进行查询,得到一个Use,这里我们就可以添加一个测试。我们将进行对比Use实例是否为空(实例化对象为空是一个很多程序员都爱犯的毛病,毛病虽小后患无穷呀),我们将每次获取的Use的id都进行记录,看是不是在Use 里面有权限,如果权限为空那后面进入Form的判断就出问题了。所以权限也要记录下来。如果id有值、权限id也有值,说明用户获取没有问题,那么程序的自我测试就可以输出“获取用户 OK”的字样。如果出现问题了,就输出“获取用户 ERROR”加上错误信息,是什么地方的问题。
上面说的是有关Use内容错误的问题,还那这个我来继续说明逻辑错误的问题。我们在进行权限判断时候发现我们有两个Form可以选择,那么如果这两个都不匹配的话不是就报错了吗?所以在进行判断的时候我们要自我测试这个判断方法。如果有相应Form的话我们就可以进入窗体并且自我测试输出“窗体匹配 OK”,如果有问题的话就输出“窗体匹配 ERROR”加上错误信息,是什么地方的问题。
我的意见的针对一个对象用一个或者多个自我测试函数方法和一种或者多种测试结果输出接口(输出接口后面会具体讲到)。
二、自我测试的输出
有了自我测试的概念我们就可以来设计具体的测试结构了,如图:
我讲输出分成两类(外加一类附加):系统输出、日志输出(和接口服务输出)。
1、系统输出。
一般来讲,在测试中我们可以将一些测试比较后的值输出到系统的控制台中,这样在运行程序的我们能够在控制台中很明显的看到每一个自我测试环节的结果是什么样子的,我习惯将正确的输出OK,错误的输出ERROR和具体信息。这样子我们也可以主要看在运行过程中的ERROR信息,必要的话可以记录全部错误信息,甚至可以和日志关联进行日志输出。
2、日志输出。
在有些时候,比如程序需要跨网段或者直接运行编译好的程序的时候,我们不可能实时都在监控控制台里的关注其内容。那么这个时候日志的就其作用了,我们通过查看日志可以实现在控制台中实现的所有功能。
3、接口输出。
当然还有一种输出没有画在图中,因为这并不是所有程序都有的,不过我这里也要提一下,他就是接口服务输出,在一些比较大的系统中会有统一日志管理系统,它会是一个单独的子系统,那么这个时候我们也可以通过调用web service等方法进行这个方面日志输出。
上面落了罗嗦说了一大推,很多人肯定已经不耐烦了吧,我曾经多次把这个思想灌输给我的同事或者上级,但是都被否定了。说来也可惜,总是一句话“程序代码还完不成呢?再写测试代码,哪有那么多时间呀!”其实他们说的没错,现在的项目赶呀赶,赶呀赶,确实没有给程序员写自测代码的时间,但是在我看来这个是软件开发必要的环节。为了提高软件质量,软件的排错能力,自测这个环节的,投入是远远小于回报的。说几个我身上发生事实,我曾经为了错误整整忙了2天,晚上都是很晚打车才回的家,为了排查错误,错后发现是在系统交互的时候,实体对象的来源出现了问题,数据来源是以XML字符串的形式来的,但是怎么也解析不出来,也数据库里也看不到错误所在,并且我们还涉及到了跨网段,调试起来十分不方便。是想一下要是有了自我测试代码的话,是不是一份钟就可以了。首先找到哪里错了,再把实体数据拿出来和schema一比较,要是想我一样还有xmlspy的话。唉,我想连一分钟都用不了。我想每个朋友都有类似的经历吧。
自我测试程序的确是增加了代码编写量,自测程序并不比代码编写少,但是当我们在写程序的时候很多都是可以顺手编写的,与上述的经历相比较,我想大家还是偏向自测程序这边,高效率的代码对我们的水平提升是很有好处的。我现在在写代码的时候就尽量向这方面发展,我现在朋友也会和我有同感的。很多事情只有试过才知道他的好。呵呵,希望大家都尝试一下,然后我们进行交流共同进步呀!