锁定老帖子 主题:什么是“测试驱动开发”
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2006-04-24
firebody 写道 robbin 写道 首先我自己也没有能够很好的进行TDD,目前还停留在边写功能代码,边写测试的阶段,不过在向TDD的方向努力
可以把那个Interceptor的代码放出来看嘛? 一个纯粹的感觉: 既然仅仅测试Interceptor那部分的逻辑,不如把它单独抽出来形成一个独立的模块,不依赖于ActionInvocation 也不依赖于xwork的拦截器,测试更加单纯一些。 不然你的代码还的需要mock action, action.execute() ...... 创建Action很容易,没有什么代价,就没有必要mock了。 测试用的Action public class RenderPageAction extends ActionSupport implements RenderPageAware { private Page page; private String wikiContent; public Page getPage() { return page; } public void setPage(Page page) { this.page = page; } public String getWikiContent() { return wikiContent; } public void setWikiContent(String wikiContent) { this.wikiContent = wikiContent; } public String execute() throws Exception { return SUCCESS; } } |
|
返回顶楼 | |
发表时间:2006-04-24
robbin 写道 public class RenderPageInterceptor extends AroundInterceptor { private WikiService wikiService; public void setWikiService(WikiService wikiService) { this.wikiService = wikiService; } @Override protected void after(ActionInvocation invocation, String result) throws Exception { Object action = invocation.getAction(); if (action instanceof RenderPageAware) { HttpServletRequest request = ServletActionContext.getRequest(); Page page = ((RenderPageAware) action).getPage(); String wikiContent = wikiService.getPageWikiContent(page.getContent(), page.getTemplate().getContent(), request); ((RenderPageAware) action).setWikiContent(wikiContent); } } @Override protected void before(ActionInvocation invocation) throws Exception { } } 看了这段代码,要是我的话,我不会专门测试这个Intercetor的,我会专门测试 wikiService.getPageWikiContent(page.getContent(), page.getTemplate().getContent(), request); 也就是wikiService的逻辑。 除了这行代码最重要外,别的都可以认为是正确的,留给集成测试或者QA去作。 另外我前面所说那一点就差不多你现在这个代码的逻辑,主要的就是抽出wikiService |
|
返回顶楼 | |
发表时间:2006-04-24
WikiService另外有自己的单独测试代码。
写这个InterceptorTest是为了重构WikiService做准备的。因为你可以看到现在wikiService需要传入一个HttpServletRequest,Service偶合了Servlet API了,这是一个不好的味道,准备把wiki render的部分抽出来放到Interceptor实现,所以就写了InterceptorTest。 另外举这个例子也有特别的用意,因为功能逻辑很简单很容易理解,但是测试代码很多,一般的单元测试不会有这么多环境搭建的代码,所以这个例子算是比较极端了。如果这样极端的例子大家都觉得可以顺畅的理解,那么我想写一般测试用例,也就没什么问题了。 顺便说一句,我其实感觉DAO的测试还是很容易写的,毕竟由于是启动spring容器连接真实数据库的;Service也很容易,仅仅需要提供DAO mock;而Action测试就要麻烦很多了,需要提供Service mock和ActionContext环境,还有request/response的stub;而最麻烦的莫过于Interceptor的测试,除了上面的环境之外,还需要mock invocation,准备valuestack,还有Action的stub。 针对大家上面的讨论,我觉得可以这样解释: 1、replay之后,verify之前的测试代码显然是需求文档,如果不关心功能代码的白盒测试,光看这一部分就足够了。 2、replay之前的测试代码显然是设计文档,,如果你关心功能代码的实现细节,就可以看这一部分。 |
|
返回顶楼 | |
发表时间:2006-04-24
robbin 写道 WikiService另外有自己的单独测试代码。
写这个InterceptorTest是为了重构WikiService做准备的。因为你可以看到现在wikiService需要传入一个HttpServletRequest,Service偶合了Servlet API了,这是一个不好的味道,准备把wiki render的部分抽出来放到Interceptor实现,所以就写了InterceptorTest。 另外举这个例子也有特别的用意,因为功能逻辑很简单很容易理解,但是测试代码很多,一般的单元测试不会有这么多环境搭建的代码,所以这个例子算是比较极端了。如果这样极端的例子大家都觉得可以顺畅的理解,那么我想写一般测试用例,也就没什么问题了。 顺便说一句,我其实感觉DAO的测试还是很容易写的,毕竟由于是启动spring容器连接真实数据库的;Service也很容易,仅仅需要提供DAO mock;而Action测试就要麻烦很多了,需要提供Service mock和ActionContext环境,还有request/response的stub;而最麻烦的莫过于Interceptor的测试,除了上面的环境之外,还需要mock invocation,准备valuestack,还有Action的stub。 针对大家上面的讨论,我觉得可以这样解释: 1、replay之后,verify之前的测试代码显然是需求文档,如果不关心功能代码的白盒测试,光看这一部分就足够了。 2、replay之前的测试代码显然是设计文档,,如果你关心功能代码的实现细节,就可以看这一部分。 所以Action就不要放过多的逻辑啊。 因为Action不好测试,所以就不要方复杂的逻辑在Action。 只要能够使得Action的逻辑非常简单,我一般都不对Action作单门的测试,用集成测试或者验收就ok了。 |
|
返回顶楼 | |
发表时间:2006-04-24
yuxie 写道 charon 写道 这么说的话,TDD中测试至少有三层含义:
1. 测试是设计文档. 对应于功能实现 gigix说的是“TDD”才是设计,但是这个也许并不能等于TestCase就是设计文档了。从传统的意义上,一提到设计,人们想到的一般是架构、层次、耦合等等高层次的东西,但是单独的一个TestCase对于描述这些想法显然是力不从心的,它并不如白板上画出的UML更有效。 当然这没有诋毁TDD的意思,TDD强调的是两顶帽子小步前进,在不断小跑的过程中通过重复代码的消除,最终得到一个好的架构。本身这是一个很精妙的过程。但是对于TestCase本身来说,不管是描述这个过程还是描述过程带来的结果,自身的能力都是远远不够的,也许最终还是要靠程序员之间的交流和快速开发指南来说明这个东西。 这个在开发过程中当然不会有太大的问题。但是如果是用在企业里面的,你交付给企业的也是这样的一堆东西,而没有相对应的设计文档,那么对于这个项目的后期维护,是一个非常大的障碍。对于一些重要的设计决策,类与类之间的静态和动态的关系,以及为什么这样设计,都是需要用一定量的文档的。否则,要从这样的一堆测试用例中理清楚相关类之间的交互方式,显然是一个相当艰巨的任务。 当然,如果是做成一个产品,就无所谓这个问题。 |
|
返回顶楼 | |
发表时间:2006-04-24
charon 写道 yuxie 写道 charon 写道 这么说的话,TDD中测试至少有三层含义:
1. 测试是设计文档. 对应于功能实现 gigix说的是“TDD”才是设计,但是这个也许并不能等于TestCase就是设计文档了。从传统的意义上,一提到设计,人们想到的一般是架构、层次、耦合等等高层次的东西,但是单独的一个TestCase对于描述这些想法显然是力不从心的,它并不如白板上画出的UML更有效。 当然这没有诋毁TDD的意思,TDD强调的是两顶帽子小步前进,在不断小跑的过程中通过重复代码的消除,最终得到一个好的架构。本身这是一个很精妙的过程。但是对于TestCase本身来说,不管是描述这个过程还是描述过程带来的结果,自身的能力都是远远不够的,也许最终还是要靠程序员之间的交流和快速开发指南来说明这个东西。 这个在开发过程中当然不会有太大的问题。但是如果是用在企业里面的,你交付给企业的也是这样的一堆东西,而没有相对应的设计文档,那么对于这个项目的后期维护,是一个非常大的障碍。对于一些重要的设计决策,类与类之间的静态和动态的关系,以及为什么这样设计,都是需要用一定量的文档的。否则,要从这样的一堆测试用例中理清楚相关类之间的交互方式,显然是一个相当艰巨的任务。 当然,如果是做成一个产品,就无所谓这个问题。 文档一样需要管理的。 如果文档作为用户的一个基本需求,那也是要做的。只不过会在后期作。 现在跟我们讨论的TDD是两回事。 |
|
返回顶楼 | |
发表时间:2006-04-24
赫赫,终于有人跳出来,讨论“测试驱动开发”了。
俺先提一个想了很久的问题: TDD要求先写出测试,然后让它通过。我的疑问是:“对于要开发的东西,除非有了充分的认识,否则,首先写测试,是很困难(如果你说不困难,那就是俺笨),该咋办?” |
|
返回顶楼 | |
发表时间:2006-04-24
firebody 写道 robbin 写道 WikiService另外有自己的单独测试代码。
写这个InterceptorTest是为了重构WikiService做准备的。因为你可以看到现在wikiService需要传入一个HttpServletRequest,Service偶合了Servlet API了,这是一个不好的味道,准备把wiki render的部分抽出来放到Interceptor实现,所以就写了InterceptorTest。 另外举这个例子也有特别的用意,因为功能逻辑很简单很容易理解,但是测试代码很多,一般的单元测试不会有这么多环境搭建的代码,所以这个例子算是比较极端了。如果这样极端的例子大家都觉得可以顺畅的理解,那么我想写一般测试用例,也就没什么问题了。 顺便说一句,我其实感觉DAO的测试还是很容易写的,毕竟由于是启动spring容器连接真实数据库的;Service也很容易,仅仅需要提供DAO mock;而Action测试就要麻烦很多了,需要提供Service mock和ActionContext环境,还有request/response的stub;而最麻烦的莫过于Interceptor的测试,除了上面的环境之外,还需要mock invocation,准备valuestack,还有Action的stub。 针对大家上面的讨论,我觉得可以这样解释: 1、replay之后,verify之前的测试代码显然是需求文档,如果不关心功能代码的白盒测试,光看这一部分就足够了。 2、replay之前的测试代码显然是设计文档,,如果你关心功能代码的实现细节,就可以看这一部分。 所以Action就不要放过多的逻辑啊。 因为Action不好测试,所以就不要方复杂的逻辑在Action。 只要能够使得Action的逻辑非常简单,我一般都不对Action作单门的测试,用集成测试或者验收就ok了。 确实是这样的,Action里面不应该放入任何业务逻辑,否则测试起来就会很麻烦。 |
|
返回顶楼 | |
发表时间:2006-04-24
firebody 写道 charon 写道 yuxie 写道 charon 写道 这么说的话,TDD中测试至少有三层含义:
1. 测试是设计文档. 对应于功能实现 gigix说的是“TDD”才是设计,但是这个也许并不能等于TestCase就是设计文档了。从传统的意义上,一提到设计,人们想到的一般是架构、层次、耦合等等高层次的东西,但是单独的一个TestCase对于描述这些想法显然是力不从心的,它并不如白板上画出的UML更有效。 当然这没有诋毁TDD的意思,TDD强调的是两顶帽子小步前进,在不断小跑的过程中通过重复代码的消除,最终得到一个好的架构。本身这是一个很精妙的过程。但是对于TestCase本身来说,不管是描述这个过程还是描述过程带来的结果,自身的能力都是远远不够的,也许最终还是要靠程序员之间的交流和快速开发指南来说明这个东西。 这个在开发过程中当然不会有太大的问题。但是如果是用在企业里面的,你交付给企业的也是这样的一堆东西,而没有相对应的设计文档,那么对于这个项目的后期维护,是一个非常大的障碍。对于一些重要的设计决策,类与类之间的静态和动态的关系,以及为什么这样设计,都是需要用一定量的文档的。否则,要从这样的一堆测试用例中理清楚相关类之间的交互方式,显然是一个相当艰巨的任务。 当然,如果是做成一个产品,就无所谓这个问题。 文档一样需要管理的。 如果文档作为用户的一个基本需求,那也是要做的。只不过会在后期作。 现在跟我们讨论的TDD是两回事。 由开发商后期补设计文档是比让其在后期补测试用例更加痛苦的过程,而且很可能不会补全,并且极大的可能是派一些文档工作者或者新手而非开发者来干这些事情。所以,从甲方立场出发,虽然我要的是一份项目验收阶段的准确的、高质量的设计文档。但是为了做到这一点,必须要求乙方能够在每个关键时间点同步提交文档。而不是说后补。其实即便在前TDD时代,后补的文档并没有任何实际的意义。 从某种意义上说,TDD是要灭绝相应的设计文档的,而这些文档如果仍然存在,那么TDD在这方面的好处就打了折扣了。如果设计文档是要和设计同步更新的,那么在消灭文档这个方面,TDD就没有足够的好处。 关于设计,TDD只能说是根据团队的平均个人能力确定预设计的粒度。对于Rod这样的牛人,粒度可以大一点,而对于偶这样的衰人,就只能慢慢来了。 |
|
返回顶楼 | |
发表时间:2006-04-24
charon 写道 firebody 写道 charon 写道 yuxie 写道 charon 写道 这么说的话,TDD中测试至少有三层含义:
1. 测试是设计文档. 对应于功能实现 gigix说的是“TDD”才是设计,但是这个也许并不能等于TestCase就是设计文档了。从传统的意义上,一提到设计,人们想到的一般是架构、层次、耦合等等高层次的东西,但是单独的一个TestCase对于描述这些想法显然是力不从心的,它并不如白板上画出的UML更有效。 当然这没有诋毁TDD的意思,TDD强调的是两顶帽子小步前进,在不断小跑的过程中通过重复代码的消除,最终得到一个好的架构。本身这是一个很精妙的过程。但是对于TestCase本身来说,不管是描述这个过程还是描述过程带来的结果,自身的能力都是远远不够的,也许最终还是要靠程序员之间的交流和快速开发指南来说明这个东西。 这个在开发过程中当然不会有太大的问题。但是如果是用在企业里面的,你交付给企业的也是这样的一堆东西,而没有相对应的设计文档,那么对于这个项目的后期维护,是一个非常大的障碍。对于一些重要的设计决策,类与类之间的静态和动态的关系,以及为什么这样设计,都是需要用一定量的文档的。否则,要从这样的一堆测试用例中理清楚相关类之间的交互方式,显然是一个相当艰巨的任务。 当然,如果是做成一个产品,就无所谓这个问题。 文档一样需要管理的。 如果文档作为用户的一个基本需求,那也是要做的。只不过会在后期作。 现在跟我们讨论的TDD是两回事。 由开发商后期补设计文档是比让其在后期补测试用例更加痛苦的过程,而且很可能不会补全,并且极大的可能是派一些文档工作者或者新手而非开发者来干这些事情。所以,从甲方立场出发,虽然我要的是一份项目验收阶段的准确的、高质量的设计文档。但是为了做到这一点,必须要求乙方能够在每个关键时间点同步提交文档。而不是说后补。其实即便在前TDD时代,后补的文档并没有任何实际的意义。 从某种意义上说,TDD是要灭绝相应的设计文档的,而这些文档如果仍然存在,那么TDD在这方面的好处就打了折扣了。如果设计文档是要和设计同步更新的,那么在消灭文档这个方面,TDD就没有足够的好处。 关于设计,TDD只能说是根据团队的平均个人能力确定预设计的粒度。对于Rod这样的牛人,粒度可以大一点,而对于偶这样的衰人,就只能慢慢来了。 如果是你们期望的一个基本需求,那就是这样的。 我说的后补,大概就是你说的关键点。 |
|
返回顶楼 | |