用SWTBot+Junit+Truth做GUI层面的Unit Testing

在开发组里,Unit Testing的方法已经深入人心,Case的数量也越来越多。可因为GUI层面代码的特殊性,目前大多数的测试都针对非GUI层面的Code。这使得占总代码量40%的GUI层面很少被单元测试覆盖。

本文通过一个简单的例子,结合SWTBot,Junit和Truth,实现了GUI层面的单元测试。

SWTBot的安装

SWTBot可以通过Update Site(http://download.eclipse.org/technology/swtbot/releases/latest/)安装。

在开发项目中也要加入SWTBot的依赖,

一个简单的Dialog

让我们来实现一个简单的对话框,用来做两个数的加法。

代码如下:

这里特别使用了setData方法对几个关键的控件设置了“id”,这是为了能在SWTBot中更方便准确的定位待测试的控件。

SWTBot+JUnit+Truth

待测的对话框已经准备好了,下面来写一个简单的单元测试。

几点要注意的地方,

  • 测试Dialog时,要使用setBlockOnOpen(false),否则open()方法会把后续测试代码阻塞掉。
  • SWTBot搜索控件的办法有几种,以Text控件为例,介绍几个常用的。
    • text()等价于text(0), 就是找第一个Text控件
    • text(n), 按顺序找第n个控件
    • textWithLabelInGroup(label, inGroup)等价于textWithLabelInGroup(label, inGroup,0),就是找在某个Group里的第0个Label为”label”的Text控件
    • textWithLabelInGroup(label, inGroup,n),就是找在某个Group里的第n个Label为”label”的Text控件
    • textInGroup(text, inGroup)等价于textInGroup(text, inGroup, 0), 找在某个Group里第0个text为“text”的Text控件
    • textInGroup(text, inGroup, n), 找在某个Group里第n个text为“text”的Text控件
    • textWithId(key, data), 找出key=data的Text控件。这就是和前面setData()相对应的一个API。用ID找还有一个更简单的API–textWithId(value),这个API没有输入key,原因是SWTBot有个Preference给DEFAULT_KEY–org.eclipse.swtbot.search.defaultKey,默认的值为
      “org.eclipse.swtbot.widget.key”
  • 对不同的控件,API会略有不同,不过大同小异。SWTBot现在除了支持基本的SWT控件外,还支持了Nebula Grid,NatTable,GEF等复杂的控件。
  • 写Assertion的部分和普通的Unit Test并没有什么不同。
  • 因为这种Unit Test的写法和普通的JUnit并没有什么不同,所以Headless Build就可以用一般maven test。当然,如果是开发RCP应用,一定要使用Eclipse Tycho插件。
  • 还有一点不同的事,SWTBot的Case一定要在X环境下。如果Build Server上并没有开X环境,需要要安装Xvfb

总结

通过一个小例子,本文讨论了如何使用SWTBot对小型的SWT开发单元,如对话框、Composite等,进行单元测试(SWTBot也可以测试完整的大型应用)。

现在可以慢慢完善GUI层面的单元测试了,下一步也许可以和Cucumber之类的Framework结合起来。😁

 

 

 

Eclipse里生成Guava-based的hashCode()和equals()

做Java开发的常常要重写类的hashCode()和equals()。一般情况下,可以套常用的模板。Eclipse就套用这些模板,直接生成默认实现,可实现比较冗长,比如下面的例子

有类A,

Eclipse的默认实现是这样的,

显然实现比较冗长,也比较乱,包含了很多细节,有些也不符合代码规范(比如if后没有花括号)。

用过Guava的同学都知道,使用Objects类可以大大简化这两个函数的实现。我试着扩展了Eclipse JDT,套用Guava的实现模板,那代码就漂亮多了。Guava隐藏了大量的实现细节。

如下,

具体的代码实现在Github上,点这

主要分如下有几步,

  • Project Explorer的上下文菜单中加入新的功能选项。
  • 通过Eclipse里事件得到Java CompliationUnit,使用ASTParser得到类里的Non-Static Member Field,然后就套用新模板吧。
  • 一个源文件中可能包含多个Java Class,所以还要有个Dialog来选择在哪个类中实现方法 。

其他细节都在代码里的,有兴趣的就看看吧。😁

别忘了在RCP App中配置自己的Keybinding Scheme!

在Eclipse RCP的基础上开发,我们会感到非常便利,有好多可重用的组件。但有时也有些意想不到的问题,  Key bindings就是其中之一。

一般情况下,我们通过扩展org.eclipse.ui.bindings来增加新的快捷键绑定。下面的例子中,我设置了一个CTRL+T的快捷键。

6-15-2015 2-25-23 PM

6-15-2015 2-25-47 PM

乍看上去,编译使用都能够做良好,问题出在schemeId上。org.eclipse.ui.defaultAcceleratorConfiguration是默认的scheme, 它里面其实包含了大量的快捷键设定,很多可能是你不要的,比如CTRL+F(查找与替换),CTRL+N(新建Wizard)等等。如果发布到产品中,反而画蛇添足。

修正这个问题很容易,就是设置自己的scheme。下面是个例子,

6-15-2015 2-32-59 PM

6-15-2015 2-33-09 PM

别忘要同时修改一下,Key的设定,

6-15-2015 2-34-25 PM

还有如果要让自定义的scheme能生效,要修改一下plugin_customization.ini文件,加入如下配置,

这样,你的Scheme就会生效了。

可能你回问,如果想重用如CTRL+S的快捷键怎么办?那就重用File Save的commandId,如下,

6-15-2015 2-40-14 PM

6-15-2015 2-39-30 PM

这样问题就解决了。

制作本地Eclipse Release P2 Repository

因为某些原因,公司的Build Server都不能访问外网了,所以不能直接从Eclipse的公共服务器上下载开发包。以前Eclipse提供所谓All-in-One package,可现在没有了。还有更方便的做法,可以下载Eclipse的所有Pacakge,生成本地的P2 Repository。

Repository包含metadata和artifact,要分别下载,但可以下载到同一个文件下。

程序运行后,没有UI,只在后台下载。耐心等待下载完成后,程序自动退出,所有的包就在destination里了。如果需要,可以传到Sonatype Nexus做成Virtual Repository (详见在Nexus OSS上建Eclipse p2 repository)。

NOTE

  • 完整的Repository非常大,下载的过程很长,一次下载不完可以分多次下载,重新键入命令即可,下载完成的包都很跳过的。
  • 下载artifact时,会有一个.blobstore文件夹,不要以为它是个临时文件夹,它也是Repository的一部分。

Reference

Manually uninstall OSGi bundle in the runtime

Sometimes in a RCP application, you may need to uninstall OSGi bundle in the runtime. With BundleContext, it’s doable. Here is a sample.

As you can see, you need to specify the bundle/plugin name.

How to extend p4eclipse plugin?

What’s p4eclipse?

Perforce provides is a very powerful and handy plugin for Eclipse – p4eclipse. With it, you can do everything in Eclipse enviornment, which is the same as in it p4 command line and p4v (a clicent of perforce).

Why to extend it?

Team also deployed ReviewBoard to do pre-check-in code review. And we have a command-line tool to post review request to it. The input is the id of perforce changelist.

Basically, we can see the changelist in Eclipse. The problem is how to combine them together and allow us to easily post the request from GUI directly.

2014-07-28_13-57-09

When you right-click on the changelist, you can see some actions in the context menu.

2014-07-28_14-02-55

Can we add another menu item into the context menu and post review request?

Ways to Extend p4eclipse

p4eclipse is also a Eclipse plugin and it follows the basic concept, such as extension, extension point, views and etc. It should be easily extended.

Find which view to extend

First, we need to find out the view class of P4 changelist. Simply move the mouse on view tab and click ATL+SHIFT+F1 to launch “Plug-in Selection Spy“. And the view name is PendingView, contained in plugin-in “com.perforce.team.ui” and the element in tree is P4PendingChangelist.

2014-07-28_14-08-36

Create plugin-in project

Now we know to extend which view. It’s time to create our own plugin-in project. And in the MANIFEST.MF, besides basic plugins, we need explicitly declare we need p4eclipse plugins,

  • com.perforce.team.ui
  • com.perforce.p4api
  • com.perfroce.team.core

2014-07-28_14-16-31

NOTE: Here is a bug of Eclipse PDE 3.x. Even we declare the com.perforce.team.ui is needed, but Eclipse can’t build through and there will be build errors. The root cause is com.perforce.team.ui is in a unpacking feature and PDE can’t revolve the dependency correctly in such case. To fix it, we have to download the jar file of com.perforce.team.ui to local and put it in to runtime class path manually. Remember, it’s only to fix PDE issue, we don’t need it in packaging.

2014-07-28_14-22-41

Add new menu item into the context menu

Open the MANIFEST.MF file and file the context menu extension declaration. It’s in org.eclipse.ui.popupMenus and the menu id is com.perforce.team.core.p4java.IP4PendingChangelist.

2014-07-28_14-26-35

Add new action into it,

2014-07-28_14-29-12

Next steps are easy

Now we finished the important part. If you are familiar with Eclipse RCP, you should understand next steps are easy. Basically, we need to launch a command line from Java code and implement an  action handler for the new created menu item. In my implementation, I also add a new preference page for team to setup the command location.

For details, please refer to my github project p4eclipse_ext.

Extened p4eclipse

After extension, the review request can be posted very easily, just like a build-in function of perforce. 😆

 

不要放过SWT里的Resource Leak

最近出了几个奇怪的问题,打开时间的GUI会莫名的抛Exception,报“no more SWT handle”. 句柄(Handle)资源以前做Win32/MFC接触的比较多,转到Java/SWT之后就很少接触到。

出错原因很清楚,Resource不够了!SWT和Swing的实现方法不同,它还是使用了Native的资源,所以句柄资源不会被GC回收。Eclipse提供了一个叫Sleak的工具来检查Leak的情况。情况果然非常严重,主要原因是我们生成了大量的Image。不仅仅是Image,在使用Font,Color,GC的时候也要非常注意,遵循“谁生成谁销毁”的原因。

在RCP中加入Sleak的方法是–Application.java的start方法中加入:

之后,Sleak的界面会随着RCP启动。

Reference

使用nexus unzip repository plugin实现Eclipse p2代码仓库

使用RCP的同学们应该都知道,p2仓库管理软件包的Layout和maven自身的不同的。当使用Export Product时, 就可以看到repository文件夹里,软件包的放置是完全不同的。

Sonatype Nexus OSS可以部署p2的proxy repository, 但不能部署host repository (当然可以出银子买Professional版). 如何在OSS版里实现呢?大家可以通过unzip repository plugin来实现。

原理

unzip repository plugin的工作原理就是:

  • 指定某个Host Repository做为目标仓库
  • 解压Host仓库里的Zip文件,形成可以访问的且保留了原压缩包中文件夹结构的Virtual仓库

利用这个功能,就间接的做出p2的仓库,主要步骤:

  • 含有p2 layout的仓库做成zip
  • 上传或安装到某Host Repository
  • 设置Virtual Repository并指向Host Repository

Plugin安装

点这下载unzip repository plugin bundle file. 解压到Nexus的Plugin文件夹中,重启Nexus Server。

 配置Virtual Repository

以Eclipse Nebula 1.0为例,配置Virtual Repository。

2014-04-17_20-55-38

Eclipse Nebula Project提供了很多SWT的高级控件,很好的补充了原有的控件集。但它自身的Repository一直处于Snapshot状态,所以经常变动,很容易把持续集成搞挂。我们就遇到了两次。

有了Unzip virtual repository,这个情况就可以改变了。先下载Nebula 1.0的zip包,上传至一个Host Repo里。

2014-04-18_11-56-01

 

再新建一个Virtual Repository,Provider选择为“Unzip Repository Template”.

2014-04-17_21-34-01

2014-04-17_21-34-31

再去查看Virtual Repo,就可以看到Zip文件被解开成p2 layout的文件夹了。

2014-04-18_12-20-51

使用Virtual Repository

在Target Platform中使用我们定义的Virtual Repository,和普通的p2 repository一样。

2014-04-18_12-23-43

总结

unzip repository plugin很大程度上提高了Nexus的灵活性,不单单只是支持Maven的layout,还可以支持p2或其他layout. 我们的项目也可以向Nexus提交Eclipse RCP的代码仓库。

好东西,与大伙分享!

About “Plug-in from Existing JAR Archives” kind projects in maven+tycho build

In Eclipse RCP project, you may need to create such kind of “Plug-in from Existing JAR Archives” projects, because you need to reuse non-OSGi jar, such as Google Guava, SLF4j and etc.

In our team, before we have to manually download the needed jar libs from website, then change build.properties, MANIFEST.MF. And of couse, these libraries should be uploaded to version control system (such as P4, Git), as others (as well as Jenkins) can sync to latest and build.

This approach has some defects:

  • Too much manual work and hard to maintain the dependencis.
  • 3rd party libs become more and more.

Here is a solution for that – using maven dependency:copy-dependencies to copy all needed libs.

tycho + maven

Tycho is a great maven plugin, which can help to do headless build for a Eclipse RCP project. It will analyze the dependencies in MANIFEST.MF and also in pom.xml. We can use the feature to define the 3rd party dependencies in pom.xml.

You can find more usefule information about tycho on Eclipse.com.

Define the needed libraries in pom.xml

Just as what a common maven project does, you can define the libraries you need in <dependencies> of pom.xml, such as,

Use maven-dependency-plugin:copy-dependencies to copy

Use the goal of maven-dependency-plugin:copy-dependencies to copy those jars to specific folder, such as,

The goal is defined at “initialize” phase.

Change MANIFEST.MF

With this pom.xml, you can do a manually build and retrieve the jars from maven repository. Then you need to put them into the bundle’s CLASSPATH and exported packages in MANIFEST.MF.

Summary

Using this way, you don’t need to worry about third party libraries in OSGi environment any more. All dependencies are defined in pom.xml. It’s very clean and clear. Of course, in real world, you need do more configuration in pom.xml (such as clean work) and an IDE environment integration.

 

 

在Nexus OSS上建Eclipse p2 repository

Eclipse p2使用了特殊的repository Layout(其实我认为就是所谓的”Update Site”)。它有别于一般的maven2 layout, Maven 3可以支持,maven tycho也可以很好的支持。但每次都要连接Eclipse的中央仓库,速度是一个问题,稳定性很不好(尤其在中国),有时会断掉。

为了解决这个问题,我尝试地在公司内部搭建了Nexus OSS,并建立了Eclipse p2的Proxy repository。

搭建Nexus OSS

Nexus OSS是流行且免费的部署私有Maven Repository的工作。安装很简单,从官网上下载最新的版本,解压,再运行$install_folder/bin/nexus start. 这个命令还不需要root权限。

TIP:最好把JVM最大内存调整一下。修改$install_folder/bin/jsw/conf/wrapper.conf, 找到wrapper.java.initmemorywrapper.java.maxmemory,分别调大一些。我的配置分别是256和1024.

安装p2的扩展

默认的Nexus OSS是不支持p2 layout的。需要在Sonatype RSO上下载相关的扩展。打开Sonatype RSO网页,搜索nexus-p2-bridge-pluginnexus-p2-repository-plugin两个扩展,我选择了2.6.3-01版本,分别下载它们的bundle zip(包含了dependencies)。再解压到$install_folder/nexus/WEB-INF/plugin-repository,然后重启Nexus OSS ($install_folder/bin/nexus restart).

配置p2 proxy repository

admin登录后,进入Repositories,选择Add->Proxy Repository,

在打开的设置页面里,输入repository id, repository name, format要选择p2, 还有设置Remote Storage Location。我的例子中选择了Eclipse Indigo.

Nexus会生成新的Repository Link,它和Remote Location可以一样使用。第一次的Index要慢一些。

其他用处

新产生的Link还可以帮助定义Target Platform。做Eclipse RCP开发的Team一定会遇到统一Target Platform的问题,说白了就是全组人的目标平台环境要完全一样。默认情况下,Target Platform是和IDE一样,所以如果A用Eclipse 4.3, B用Eclipse 3.7,可想而知那是多么混乱。

Eclipse PDE想到了这个问题,我们可以定义Target文件,在里面详细定义目标平台的构成,包含哪些Feature,哪些Plugin。有了内部的Repository,在Target文件里可以直接引用相应的连接,非常的稳定!

总结

Nexus OSS里可以添加p2 proxy repository,可以大大提高项目在编译时的速度和稳定性,提高生产效率。但它不支持做Host Repository,那是Pro版本里的功能。