nighty

折腾的年华
posts - 37, comments - 143, trackbacks - 0, articles - 0
      用Flex做企业应用将近有一年时间了,这个过程很累,在国内这方面的积累不多,真正有参考意义的资料的确非常少。经过一段长时间的摸索后,多少也积累了一点经验,就最近的关于单元测试的想法做一点总结,由于涉及的知识较多,这里也只是给出个人的一种思路。
     众所周知,Flex的缺点是开发调试效率较低,而且它只是表现层的一种解决方案。在企业应用中最需要解决的是编译生成的swf体积问题,我想任何客户都很难接受一个企业应用全部打包在一个swf里,几MB甚至几十MB的初始化过程谁都无法接受,所以都必不可少地采用Module的加载方式,把不同的业务功能编译成独立的swf,需要用的时候再去加载。把核心功能、通信机制、公共组件设计成库项目,编译成swc做为RSL让业务模块共享调用,达到尽量减少业务模块编译体积的目的。在这方面如果用心优化的话,基本上可以控制到每个swf体积大概在200KB以内,这样就算是互联网方式部署,客户访问仍是可以接受的。
    

     该结构图给出了一种整体的设计方案,Flex的启动肯定得有Application,这个是用户初登录后第一个加载的swf(登录就不要用flex了,用jsp或是模板实现吧)。所以它负责加载你设计的整个框架,包含模块加载机制、通信代理方式、基础库初始化等等,而和Java端的通信目前比较有效的仍然是blazeds,这个技术需要的介绍内容不在本文的范围之内。关于通信接口的实现有一种非常有用的方式就是借用Java的动态代理理念,Spring有一个flex的扩展子项目叫做springactionscript,而这个项目又引用了as3commons的库(类似于apache commons的一些公共组件)。为什么提及这个,因为flex本身的反射功能api非常难用,所以as3commons就做了扩展,它大大简化了反射的使用,而且提供了一个bytecode的工具用于操作字节码,它是实现动态代理的关键。至于为什么要动态代理?目的就是达到在写和Java对接的接口时,可以只声明接口,不需要实现类(得减少多少重复代码呀?),而和Java对接接口我们又可以开发一个工具让java code 自动转成 as code,如果懂得Eclipse插件开发的话还可以进一步做一个插件,达到Java只写一次就可以自动生成对应的flex接口,提高开发效率。
     转入正题,关于单元测试的概念,Flash Builder在4.5已经把flex unit作为内置库了,这点和Eclipse把junit内置类似,而flex unit的使用网上有大量的资料介绍,这里也不多说。flex unit在测试as代码还是不错的,和junit测试一样,提供了一些简单的Assert断言,但是你最痛苦的却不是as的测试。企业开发的特点就是数据量不大,但是需求坑爹,经常变来变去,而且结构复杂,往往一张表很多字段,关联子表,层级属性多。而你如果选择了Flex做了展示层的技术,那必定是看中它比HTML + CSS + JS更强的界面交互功能。的确,这点不容质疑,Flex Spark的皮肤机制的确提供了很多优秀的特点,不过如果你想纯熟掌握它的整个机制,恐怕得花很多时间阅读源代码才行,而皮肤的制作整对别想让美工独自实现,它同样是需要技术积累的,介绍它需要用几个篇幅才足够。任何技术方案都一样,BS、CS、AIR在实现复杂界面时,对于开发人员来说,最痛苦的莫过于界面的单元测试。
     痛苦在哪里?回看上面那幅架构图,业务功能界面实现在Flex,业务逻辑在后台Java,那么当二个团队同时进行工作的时候,沟通就是最大的成本。解决沟通的问题就必须在先前设计时约定好接口和数据结构,那是会影响双方团队协调的关键因素。当双方同时进行开发的时候,势必存在前台依赖后台的情况,因为它能到达界面的前提得在整个框架载入后(并且可以初始化一堆数据,发生了通信),Java后台还好说,依赖于spring和junit可以做到很好的单元测试。而flex就痛苦了,我没有通讯啥都做不了呀!
     如何设计单元测试?最大的出发点就是如何切掉和后台通信连接,看下面的简单结构图

     实现思路介绍:
     1.  简化Application加载过程   --   可以套用你主程序中的加载过程,但是不需要你的主界面其它多余的元素,达到减少到达测试界面的多余步骤(尽可能少地减少鼠标和键盘操作)
     2.  定义测试配置   --   测试哪个模块?哪个工作流程?你得通过配置的方式来定义,而不是每次都手写代码,才能方便你的成员使用你这个工具
     3.  模拟后台接口实现  --  记得上面说的动态代理吗?其实是为接口动态生成一个实现类,然后注入真正通信的实现代码,例如WebService、HTTP,既然可以注入这些通信渠道,当然就可以注入本地实现类啦
     4.  对象查看器    --   这个是神马?因为你都不要Java后台了,每次操作一个界面后得提交数据吧?没有后台了,提交到哪里?你得必须把你的提交对象用界面展示出来吧?好吧,这个可是个难点!

     我想这四个方面的原则无非就是:减少单元测试需要进行的步骤(最快到达测试界面),脱离后台依赖(自己简单模拟后台实现,可惜flex没有类似java的mock库,悲剧!),如何查看提交到后台的结果。 单元测试的目标:界面能正常加载、提交数据正常,如果二者都没问题,那么联调的时候就可以非常容易定位到是Flex的问题还是Java的问题!达到介分责任的目标,当然,如果你所在团队是按模块分的,也就是说flex和java都是同一个人做,那么就不存在责任问题。

     怎么实现上面的四个步骤呢?简要地介绍一下吧。
     第1简化application加载,其实你可以把第一张图中的application加载机制拷贝过来,只是主界面可以做得非常简单,比如不需要多余的控件(比如过长的菜单、当前登录人、时间、一陀设置按钮等),只留下最核心的能到达你测试界面的入口,至于怎么设计这个简化版的application,那得发挥你本人的创造力,另外还得看具体的业务。

    第2定义测试配置。模块如何加载?通信接口本地模拟实现类定义?通过配置显示在appliation做为触发控件,这些你都得自定义一套xml之类的文件来配置吧,这个就需要技巧了,不能设计得太复杂,因为你的开发人员需要沿用你定义的规范来定义它需要测试的模块,关于这方面的知识,可以参考spring加载配置文件方式、struts2加载定义文件等理念,有一个概念我比较推荐,就是struts2中的include配置文件,允许配置文件分散,让大家提供代码和文件时减少冲突,又可以套用你正常的加载机制。

    第3模拟后台接口实现。这个是比较烦的,模拟机制本身通过动态代理倒是不难实现,恶心的是你得自己动手用flex简单实现一次后台生成数据、处理数据的逻辑。这里我有个实践的总结经验分享,在前期你调试完的后台接口证明是没有问题的,那么可以混合使用,一部分调试过的接口可以直接用后台,而新接口才本地模拟。一个原则就是后台有的,已经证明稳定的就用后台,没有的或是后台还没有完成的你就自己模拟。

    第4对象查看器。想想flex不能操作数据库、由于安全限制不允许直接操作文件、无法读取本地文件目录。而你的测试数据也许会有关联(特别是在工作流方面),所以你得想一个方案来保存你的对象结果,而且得以一种人性化的方式查看对象内容。且抛开数据存储的问题,这个对象查看器如何设计就够你头疼的了,首先是对象得定义成一种格式,一种人可以看得懂的格式,比如xml,可以支持序列化和反序列化,你得去掉多余的无用属性和访问器。又得回到反射机制上了,序列化其实不难,难的是反序列化时如何正确地转成原来的对象。列一种本人设计的结构:
     <xxx   type="com.xx.oo.XXClass">
          <aa type="String">aaa</aa>
          <bb type="Boolean">true</bb>
          <list type="mx.collection.ArrayCollenction">
              ....
          </list>
     </xxx>
     对象分简单对象、复杂对象、动态对象等,如何表达这种结构和保证序列化时不丢失数据需要细心考虑。那么最后如何实现查看器呢?其实有一个参考的范例,就是Eclipse的“大纲”视图,经过实践的扩展,把树视图换成表格树(这种控件原生没有,有第三方的可以拿来修改),看个样图吧!
 
     因为你关注的对象内容无法就是这三个方面,属性名、值、和类型,又支持以树方式导航对象,已经足够你人眼分辩内容了。至于如何有效的保存测试数据,并且组织好结构,这个方面我目前也仍在思考中,未有较好的思路。
     以上内容仅是出于本人的一种方案,也许有更好的实现方案,只是水平不足以超过这种认识,希望后续能进一步思考能实现更加完美的单元测试框架。
     ST测试更关注的界面的自动化测试,这方面涉及的知识更多,一般公司是很难有财力和技术去支持做自动化测试,属于比较高端的范围,实现是很多回归都靠测试团队人肉在实现。


刚进场的时候戏就落幕

Feedback

# re: Flex在企业应用中关于单元测试的一种设计思路  回复  更多评论   

2012-04-29 10:25 by tb
准备学习测试 有点用

只有注册用户登录后才能发表评论。


网站导航: