场景一:
个人喜欢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,形成如下的代码:
[code]
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();
}
}
}
[/code]
后面又发现这个变态的一流系统居然用自己的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:
[code]
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));
[/code]
没有讨厌的expectAndReturn,不用担心parameter matcher。
场景二:
[code]
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);
[/code]
于是,对close(), startTransaction, commitTransaction(),我们都直接调用ConnectionManager, 而对其它的method则委托给realConn。
这个Implementor类是纯java代码,除了dynamic proxy没有用任何其它技术。(没有enhancement,aop之类的)
个人感觉还是挺有用的。大家点评一下?
个人喜欢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,形成如下的代码:
[code]
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();
}
}
}
[/code]
后面又发现这个变态的一流系统居然用自己的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:
[code]
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));
[/code]
没有讨厌的expectAndReturn,不用担心parameter matcher。
场景二:
[code]
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);
[/code]
于是,对close(), startTransaction, commitTransaction(),我们都直接调用ConnectionManager, 而对其它的method则委托给realConn。
这个Implementor类是纯java代码,除了dynamic proxy没有用任何其它技术。(没有enhancement,aop之类的)
个人感觉还是挺有用的。大家点评一下?