Archives
-
初涉敏捷编程
我们都知道一个设计良好的程序是优雅的,我们也知道单元测试是很重要的,有充足的单元测试就可以尽可能的保证一个程序模块的正确性。这两部分是一直困扰我的部分,如何保证一个设计是尽可能的优雅的,还有就是让单元测试测试到尽可能多的测试点。以前的做法都是提前对程序进行设计,然后进行编码,然后编写单元测试代码,再根据觉得哪里不妥修改代码,修改测试用例。 这里我遇到的问题是 设计阶段不能设计的足够精细这一点非常令我恼火,有的想要给别人看的代码,比如开源,要求你的API设计来不得半点马虎,要设计的很合理。前期的脑纸笔的设计方式,会让细节偏差很大。而合理的API基本上都是通过不断的编码得到的。 单元测试用例并不能覆盖几乎100%的代码当你写好了代码之后,你就懒得写测试用例了,尤其是针对一些不总用到的功能。而写出的用例大多都是一个非常完整的场景,而不是针对某一个小模块的测试。针对性不强。 针对这两个让我极其愤恨的问题,我决定使用敏捷编程,测试先行。 我对测试的看法一直是测试(我承认这是一句废话),我以前都是写了代码(也就过了设计阶段)再写用例,这样程序的API设计大都是凭借对代码的理解捏造出来的,理解的深,功力深,API设计的就相对好一些,理解的不够深,捏造的成分就大,后期自己看着都恶心。但是Kent Beck和Uncle Bob告诉我们测试用例就是在进行程序的设计。其实,一个好用的API,都是经过实践检验的,这样自然要不断的编写和修改代码,而我们把这个过程提前,放在最初要设计的测试用例里面。一个测试用例,就是对一组API的调用的描述,也就是场景重现,这阶段已经就是在使用自己的API,也就是检验自己的API。当然,最初的这部分编码连编译都无法通过,因为你要求的所有东西都没有,但也正因为如此,你才能按照你最舒服的调用方式调用API,而不是受到什么限制。 例如 Query query = xxDAO.createQuery(“Target”); query.addExpression(Expression.eq(“name”, “Testing”); List l = xxDAO.doQuery(query) 这时候,我们没有Query,没有addExpression,没有Expression,什么都没有,但是这代码看着很自然。这就说明如果我们这样设计API就还算不差。下一步很自然的,我们要让这部分代码编译通过,增加Query这个interface[1],然后完成编译。 我认为,到了这个阶段,程序的设计已经完成了80%了,剩下的就是看哪里能够变得更加合理,让臭味更小了。至于所有的实现类的编码,我觉得就是体力劳动了,虽然体力劳动也很累很耗时,但毕竟能够找到解决方法。这时候剩下的事情就简单多了,编写真正的实现类代码,让单元测试通过。 我们应该在编写真正的代码的时候,都是针对测试用例里面使用的API进行编码。这句话的意思就是,我们编写出的方法,应该是测试用例用到的,而不是额外的编写一些暂时没有用到的代码。如果这些将要编写的类/方法没有被测试用例覆盖到,那么要么是这些类/方法暂时没有需求,要么就是测试用例覆盖度不够。哪怕是一个简单的setter/getter,也要有测试用例直接或者间接覆盖到。之所以这样,只是为了避免破窗原理,一个没写,就会有第二个,就会有第n个。 [1]:之所以增加的是interface,就是因为增加的interface更加抽象,行为更加的单纯,并且在测试的时候,可以充分的使用Mock。但是这不是绝对的,在一些纯粹的逻辑的东西,就完全是一个class就能代替的,而无需interface,比如例子中的Expression,用class就比interface好很多
Apr 30th, 2008 | Filed under 程序设计