先使用你的设计而不是先实现你的设计----TDD
TDD(Test Driven Development)测试驱动开发,这里的测试可以理解为先使用,先写测试就要求你站在代码用户的角度思考,而不是单纯的作为一个实现者;当你站在使用者的角度时,你会因为自己要使用他们,而想方设法的使自己的设计更有用、使自己的接口更一致.
同时先写单元测试有助于消除过渡复杂的设计,因为程序员很容易走一个极端,就是做一些复杂但是不必要的事情,我们应该记住一个原则就是:如果不是真正的需要这个功能,那么我们不应该实现他,那么什么时候我们才能知道这个功能是不是必要的功能呢,OK!直接使用他们,你不使用他们就永远也不知道他是不是真的有用,一旦你先编写实现代码,你将强制自己保留这些代码,并继续使用他们,OK!接下来的问题就是:你的类越来越多,你的实现越来越负责,最后你自己也无法控制,一旦出了问题,你不得不从头开始梳理你的代码,一行一行的Debug,反过来,如果你先使用他(先写单元测试)你将深思熟虑将如何使用他,如何是他们更具有可用性和便利性,如何是自己的设计更加注重实效,从而简化你的代码,因为我们都知道好的设计并不是类越多约好
那么如何做TDD
记住几个原则
1.开始的Story设计一定是战略性的设计而不是战术性质的;
如何理解:就是说在做Story设计的时候,你应该只是设计为了实现你的功能,你需要几个类,每个类做什么事情,而不应该具体是设计你的程序的方法、参数、字段或者对象之间如何交互,因为这些是你在做具体的实现的时候才去考虑的事情,说到这里就不等不提醒大家应该走出一个误区:设计不是一开始就固定好的,他是随着你的开发具体展开的;设计应该是正确的而不是精确的,设计应该是指导你向正确的方向前进,而不是标识具体的路线也不是操纵你,设计应该是满足实现即可,而不是越详细越好;
2.设计是可以被改变的,不要让开头的设计束缚你,但改进之后的设计请知晓团队的所有成员;
3.如果不是真正的需要,请不要实现他;
4.不要自己一个人编码,而应该是让大家都参与进来,具体你可以和团队的成员讨论如何使用你的功能,你可以和专业的测试人员讨论你的AT和UT,你应该请团队程序来检视你的UT代码等等,反正不要出现:当某天你提交Story验收的时候,团队中没有一个人知道你干吗了,当然如果团队的成员不理睬你,那就没办法了,这个时候你应该思考下自己的团队协作能力是不是有问题,如果不是你应该帮助你的团队去关注每个人,如果还是不行我觉得你应该退位让贤;
5.记住这个时候你是代码使用者,你应该考虑怎么去使用,请不要让实现细节干扰你;
下面举个小小的例子:
我接到一个Story是在DataModel中加入我们客户被法院执行的的数据,这些数据包括:执行法院、立案时间、案号、执行标的(元)、案件状态、身份证号码以及数据获取的时间,为了实现这个Story我不得不设计一个网页爬虫,根据客户姓名和身份证信息从人民法院的网站上爬取数据;
基于系统现状和法院网站的数据结构我设计了以下几个类及他们的基本功能:
Customer:客户级别信息封装类
CustomerBaseInfoExecuteService: 从JSON文件中解析获取客户的基本信息
CustomerCourtDataFetchService: 根据客户的姓名和身份证信息从人民法院网站上爬取数据
CustomerCourtData:法院数据封装类
FileService:将结果保存为JSON文件
OK,下面我先来使用这些类,也就是写我的单元测试:
public void testCustomer(){
assertNotNull(newCustomer());
Customercustomer = new Customer();
StringcustomerName = “张三”;
StringcardNum = “888888888888888”;
customer.setCustomerName(customerName);
customer.setCardNum(cardNum);
assertEquals(customer.getCustomerName(),customerName);
……
}
你可能还会做这样的考虑,因为身份证号码是有格式,是不是要验证一下呢?
记住我们的原则------如果不是真正的需要,请不要实现他;
我们从数据来源去考虑这个问题,客户的身份证信息的源头是CRM,中间并为经过再次计算,也就是说我们的源头已经给我们做了这样的事情,我们并不需要在做这种重复的事情;
OK,接下来我来使用CustomerBaseInfoExecuteService:
public void test CustomerBaseInfoExecuteService(){
Filefile = new File(“src/test/resources/file1.log”);
Customercustomer = customerBaseInfoExecuteService.execute(file);
assertNotNull(customer);
assertEquals(customer,getCustomerName(),”易卫国”);
assertEquals(customer.getCardNum();” 33012519600602641X”)
}
上面是基本的流程,思考一下,作为这个类的输入file,如果他是不存在应该怎么,如果文件格式不符合JSON的格式会这样,也许你要考虑这么多用户我是不是不应该一个用户一个文件等等,于是你还应该有诸如下面的使用或者修改
List< Customer> list = customerBaseInfoExecuteService.execute(file)
assertNotNull(list);
assertEquals(list.size(),100);
……
好了,现在我已经使用了我所有在战略设计阶段设计的类,我要开始编码了,这个时候就展开我的设计,我考虑我的对象交互等问题……
以下我的一些体验:
1.我对上次那个顾问这句话持保留意见:TDD是高手的事情;我觉得TDD应该是帮助我们去思考我们的业务,思考我们代码的可用性和便利性,它重在坚持,重在积累;
2.TDD的代码和Story提交验收或者代码CheckIn的时候的测试是不等同的,TDD帮助你将设计展开,但是你还是要在代码Check in之前进行测试,这时候的测试不仅仅是单元测试,有可能是你在浏览器地址栏里输入个这样的URL: http://10.249.201.129:8080/ctu3/runService?eventType=scan&customer_start_row=0&customer_row_size=100&target_event_type=main
然后验证一下数据库或者是页面上的值是不是期望的值,也有可能是你在页面上点一个按钮等,当然也可能是更多的单元测试;
3.TDD的代码是具体实现代码的守护代码(就像守护线程一样),一旦你去修改代码守护代码就会盯着你,告诉你这样该是不是对的(当然请不要为了迎合你的错误去修改单元测试的代码,除非这个单元测试本身是错误的,对与错由客户/业务说了算);
4.TDD帮助我们思考哪些是真正需要的功能,帮助我们真正的做事,做正确的事,而不是发上一天的事情去实现一个并不需要的功能这也是敏捷的目的之一;
5.敏捷帮助我们尽快的获取反馈,在问题刚刚出现的时候解决他,你可以将你的TDD代码告诉所有的团队程序,请他们进行CodeReview,这个时候往往问题就出来了,而不是当你的实现写了99%的时候,发现其实你不需要这样做,你只要每次给个标识位问题就解决了,那么你为了这99%的实现所发的两条甚至一周的时间就是浪费;
6.不要压缩你写TDD的时间,如果你发现这个Story的TDD不好写,就是说你开始设计出来的类不要使用,那要么是你的设计错误,要么是客户给了你一个不明确的需求,但绝对不是TDD错了;
原文链接:/javaschema/287012.html