编写可测试GWT代码的一些小经验

当我们谈及可测试的GWT时总是绕不开MVP模式,一些很经典的帖子里面介绍了使用MVP模式把复杂逻辑控制在Presenter中,将展示相关的逻辑(主要是涉及到GWT native代码的部分)控制在View中。这样就能够使得对主要逻辑代码的测试摆脱GWT-Testcase的束缚,尽情的使用Junit和各种Mock所带来的强力功能。遗憾的是这里有一个完美的假设:假设我们能够把所有native代码控制在View中。而实际开发中我们发现,想做到这一点非常困难。例如我们经常要在Presenter中做一些Alert或者Confirm的工作,又或者我们的一些遗留代码中使用了native方法,特别是这些方法如果使用static方法提供调用的,那简直就是异常噩梦。

把这些代码全都转移到View层中会让Presenter的代码变得支离破碎。对于这类问题我们设计了称之为Barrier的解决方案,顾名思义:Barrier是一种篱笆,在打包运行时他能够把用到了反射、mockGWT无法编译的代码单元隔离出来;在测试阶段,又可以把GWT native代码隔离开。下面是一个针对Window类的Barrier的基本结构:

 

编写可测试GWT代码的一些小经验 - robotz@yeah - IronRobot
  思路很简单,抽象一个接口出来,包含对 Window 的基本方法调用的封装。使用 GWTWindow 类实现一个 ClientImplBarrier 的代码也很简单,就是一个单例方法:

 

 

 

public static WindowInterface window;

public static WindowInterface getWindow() {         if ( window == null )

            window = new WindowObjectBarrierClientImpl();

        return window;

    }

 

So easy 哈,Barrier发挥作用的关键就在这里。我们可以在unit testbefore-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方法达到这个目的。

下面是一个针对同步调用的rpcservicecreate的实现。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层的代码,非常好用。

 

当然这还是在一个大前提下:把PresenterView做清晰的划分,这是一把双刃剑。让我们写代码的时候不再那么随心所欲,但同时也能使我们能理清自己的逻辑,写出更漂亮,更整洁的代码。

实现PV的清晰划分并不困难,我们需要明确的是PV的纽带是什么:P响应V触发的事件,V展示P的数据。所以我们是有一些原则来保证接口的明确划分的:

例如:避免V接口的方法声明中包含如getButton之类的方法。

 

另外网上有个老外做的套件叫GWT-test-utils,使用的是javaagent技术,在类加载时对native方法重写,很强大。感谢**同学总是能发现非常好用的工具。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值