当我们谈及可测试的GWT时总是绕不开MVP模式,一些很经典的帖子里面介绍了使用MVP模式把复杂逻辑控制在Presenter中,将展示相关的逻辑(主要是涉及到GWT native代码的部分)控制在View中。这样就能够使得对主要逻辑代码的测试摆脱GWT-Testcase的束缚,尽情的使用Junit和各种Mock所带来的强力功能。遗憾的是这里有一个完美的假设:假设我们能够把所有native代码控制在View中。而实际开发中我们发现,想做到这一点非常困难。例如我们经常要在Presenter中做一些Alert或者Confirm的工作,又或者我们的一些遗留代码中使用了native方法,特别是这些方法如果使用static方法提供调用的,那简直就是异常噩梦。
把这些代码全都转移到View层中会让Presenter的代码变得支离破碎。对于这类问题我们设计了称之为Barrier的解决方案,顾名思义:Barrier是一种篱笆,在打包运行时他能够把用到了反射、mock等GWT无法编译的代码单元隔离出来;在测试阶段,又可以把GWT native代码隔离开。下面是一个针对Window类的Barrier的基本结构:
![编写可测试GWT代码的一些小经验 - robotz@yeah - IronRobot](http://img5.ph.126.net/1_urnLf1uZEYRn-o55P-Fw==/593912200876622075.jpg)
public static WindowInterface window;
public static WindowInterface getWindow() { if ( window == null )window = new WindowObjectBarrierClientImpl();
return window;
}
So easy 哈,Barrier发挥作用的关键就在这里。我们可以在unit test的before-class中把HostImpl作为初始值赋值给windw属性。这里的HostImpl我们可以做一个简单实现,把参数打印到控制台上,或者缓存到List里面以备后面进行验证,也可以Mock一个实现出来,统统OK。(HostImpl不要放在和ClientImpl同一个目录的地方,不然GWT编译就通过不了啦)。当然需要为了隔离开而大费周折的类并不多,弄一个BarrierFacade,把常用的类都包装好,维护成本并不大。对代码的侵入性也很小。
遗憾的是,这样做我们就不能保证window属性的隐蔽性了。暂时没有想到好的解决方案。
另一种需要我们大费周折隔离的就是RPC调用,GWT.create一下下就可以获得一个远程调用所需要的对象,大神奇。但写单测时怎么办?如果你的Presenter中涉及到需要与服务器交互,disaster。仔细想想,其实就是所谓的RPC总是万变不离其宗的,client端把参数发到server端,server端把结果返回回来。测试时需要网络吗?需要容器吗?当然我们不需要,更不希望需要。所以我们完全可以把网络去掉,把create生成的对象和server端的实现直接绑定在一起。我们可以借助CGlib实现一个create方法达到这个目的。
下面是一个针对同步调用的rpcservice的create的实现。Impl是一个service的实现。可以直接接上我们具体的servie的实现,不过最好还是用mock对象吧~
public static <T> T create(Class<T> rpcInterface, final T impl) { Object t = Enhancer.create(Object.class, new Class[] { rpcInterface }, new InvocationHandler() { @Override public Object invoke(Object self, Method method, Object[] args) throws Throwable { String methodName = method.getName(); Class[] argTypse = method.getParameterTypes(); Method implMethod = impl.getClass().getMethod(methodName, argTypse); if (implMethod == null) { throw new NoSuchMethodError(methodName + " not support in class" +impl.getClass().getName()); } return implMethod.invoke(impl, args); } }); return (T) t; } |
针对异步调用只要继承一个RequestBuilder并重写send方法就o啦,这里就不贴了
通过以上两个方法,我们用junit测试覆盖了大部分的Presenter层的代码,非常好用。
当然这还是在一个大前提下:把Presenter和View做清晰的划分,这是一把双刃剑。让我们写代码的时候不再那么随心所欲,但同时也能使我们能理清自己的逻辑,写出更漂亮,更整洁的代码。
实现P和V的清晰划分并不困难,我们需要明确的是P和V的纽带是什么:P响应V触发的事件,V展示P的数据。所以我们是有一些原则来保证接口的明确划分的:
例如:避免V接口的方法声明中包含如getButton之类的方法。
另外网上有个老外做的套件叫GWT-test-utils,使用的是javaagent技术,在类加载时对native方法重写,很强大。感谢**同学总是能发现非常好用的工具。