锁定老帖子 主题:我的酒窝
该帖已经被评为精华帖
|
|
---|---|
作者 | 正文 |
发表时间:2006-12-10
个人喜欢state-based的单元测试。(定义见mock is not stub)。 可是有些时候,比如在测试一些使用java.sql.Connection, ibatis SqlMapClient等接口的类的时候,明显写stub很不好。(1,接口很大,有很多不相干的东西。2,版本一旦变化,这些接口可能跟着变化,如果写stub的话,就意味着stub要跟着这些第三方接口变化) 于是,只好mock。只好interaction based。只好每回内部实现一变就死盯着一坨坨的expectAndReturn找不再有效的expectation。 场景二: 一个遗留系统用自己的连接池,使用ConnectionManager.checkIn(Connection)来释放连接(而不是Connection.close) 为了重构,希望能够重载Connection.close(),让它调用checkIn()。 跟场景一的提到的原因一样,不希望直接写一个MyConnection implements Connection。于是只好自己写dynamic proxy,形成如下的代码: private Connection conn; public Object invoke(Object proxy, Method method, Object[] args){ if((args==null || args.length==0) && method.getName().equals("close")){ ConnectionManager.checkIn((Connection)conn); } else { try { return method.invoke(conn, args); } catch(InvocationTargetException e){ throw e.getTargetException(); } } } 后面又发现这个变态的一流系统居然用自己的startMyTransaction, endMyTransaction(), rollbackMyTransaction()来搞事务处理! 于是又要继续判断method.getName().equals("startTransaction"), method.getName().equals("commitTransaction")等等等等。 dynamic proxy的代码难看的要死。 最近终于受不了了。愤然写了一个叫做酒窝的东西(dimple)。 所谓dimple,就是dynamic implementor的意思。我要用纯java语法来实现我想实现的方法(不是expectAndReturn,也不是if(method.getName().equals("method1"))),但是同时我又不愿意写“implements TheInterface”,以避免被迫处理那些我不关心的method。 用法如下: 场景一: 这样实现我的stub: MyParameter myparam = ...; SqlMapClient client = (SqlMapClient) Implementor.proxy(SqlMapClient.class, new Object(){ public Object insert(String id, Object param) { assertEquals("test id", id); assertSame(myparam, param); return null; } }); assertNull(new SomeClassUsingSqlMapClient(client).runSomeInsert(myparam)); 没有讨厌的expectAndReturn,不用担心parameter matcher。 场景二: final Connection realConn = ...; Connection conn = (Connection) Implementor.proxy(Connection.class, new Object(){ public void close() { ConnectionManager.checkIn(realConn); } public void startTransaction(){ ConnectionManager.startMyTransaction(realConn); } public void commitTransaction(){ ConnectionManager.commitTransaction(realConn); } }, realConn); 于是,对close(), startTransaction, commitTransaction(),我们都直接调用ConnectionManager, 而对其它的method则委托给realConn。 这个Implementor类是纯java代码,除了dynamic proxy没有用任何其它技术。(没有enhancement,aop之类的) 个人感觉还是挺有用的。大家点评一下? 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2006-12-10
嗯,不错,挺有用的。动态类生成,字节码操作加上Annotation给Java类型系统带来的metadata,绝对是Java框架突围的方向。俺也在做类似的东西。思路很类似,哈哈。
|
|
返回顶楼 | |
发表时间:2006-12-10
i like this code.
Nice and elegant. why not share? |
|
返回顶楼 | |
发表时间:2006-12-10
ajoo开个开源项目吧,这次俺报名参加。
|
|
返回顶楼 | |
发表时间:2006-12-10
后面一段没太看明白,dimple究竟用来干什么的?怎么看来看去还是对connection的close等几个方法委托给其他类而已呢?
|
|
返回顶楼 | |
发表时间:2006-12-10
揣摩圣意,我来替ajoo回答一下。首先ajoo表明了自己的态度,自己是支持state-based测试的。所以呢,推论是mock我是尽量不用的。那么easymock这样框架拿来做什么用呢?用来做stub。为什么要做stub呢?有些接口难以用手工实现(类的方法太多)。有些行为难以重现(比如费时)。但是我又不喜欢这些mock框架为assert行为创造的所谓优美的语法……,于是酒窝就浮现在了ajoo的脸上。
而且还顺带支持了一下真实行为的委托。这样就可以做前条件后条件的assert了,另外还能部分替换行为。(貌似rmock很标榜这几个feature?)。关键是实现还简单。 |
|
返回顶楼 | |
发表时间:2006-12-10
taowen 写道 ajoo开个开源项目吧,这次俺报名参加。
是想弄个项目的。可是一共就一个类,加上javadoc 400行代码,怕惹人说:介,介也要叫project? 关键是不知道要往上添加什么功能了。 嗯,忽然想到几点: 1。上java 5,加上点generics.(现在是java 1.3就能跑) 2。如果弄一些annotation,比如@Run(times=1),就可以同时也支持interaction based test了。 3。上cglib,支持动态override class。 这么一说,倒也可以弄个项目。 不过codehaus每回一个月的申请周期想起来有点发怵。推荐一个好的host先? |
|
返回顶楼 | |
发表时间:2006-12-10
Lucas Lee 写道 后面一段没太看明白,dimple究竟用来干什么的?怎么看来看去还是对connection的close等几个方法委托给其他类而已呢?
dimple基本上不干什么。那个委托还是dimple的client代码干的。 dimple做的,只不过是让你用更简单,更java的语法来做这个委托加部分替换的功能。 |
|
返回顶楼 | |
发表时间:2006-12-11
ajoo 写道 不过codehaus每回一个月的申请周期想起来有点发怵。推荐一个好的host先? 国内的javascud.org 不错,有整套的Subversion+Jira+Conlunce Wiki 用户整合系统。 SpringSide.org.cn也提供合作项目、孵化器项目的机制,大家如果项目较小,懒得自己架站,或者懒得辛苦作宣传时也是一个选择。 springside的PV比起javaeye当然差距巨大。但每天尚有1万左右,应该能保证大家这些小项目的宣传度和下载量,而且在scud的JavaScud和老曹的满江红赞助下,也撰齐了全套的svn,conluence wiki,jira,论坛,blog,cms....:) |
|
返回顶楼 | |
发表时间:2006-12-11
springside这个名字听着就是spring相关的。而dimple和spring无关啊。
|
|
返回顶楼 | |