« 2009年1月 | トップページ | 2009年3月 »

2009年2月の3件の投稿

2009/02/15

FESTを使用してJava GUI (Swing)のテストを実行してみる

FEST-Swingを使用してみた手順を記載する。なお、NetBeans用プラグインも存在するが、現時点ではリリースされているバージョンより古いものがインストールされる。ライブラリを自動で作成してくれる程度のことしか行っていないように見えるので、プラグインにこだわる必要は無いだろう。

環境設定

テストライブラリにfest-assert-1.0.jar, fest-reflect-1.0.jar, fest-swing-1.0.jar, fest-util-1.0.jarを追加する。(これらはプラグインに含まれていたものであり、実際に全て使用しているのかは未検証)

fest01

恒常的に使用するのであれば、ライブラリ化しておくと良いだろう。

 

1つのチェックボックスの状態をテストする

チェックボックスを用いてテストを行ってみる。今回はJUnit4.5を使用した。

以下がテスト用のウィンドウである。JFrameの中にチェックボックスコンポーネントを1個配置した(ソースコード)。

fest02

 

まず、テストケース実行前後に以下の処理を実行させる。

    @Before
    public void setUp() {
        window = new FrameFixture(new NewJFrame());
        window.show(); // shows the frame to test
    }

    @After
    public void tearDown() {
        window.cleanUp();
    }

続いて、チェックボックスが選択された状態になっていることを確認するテストケースを作成する(ソースコード)。

    @Test
    public void testCheckBox() {
        window.checkBox().requireSelected();
    }

ここまでのNetBeansプロジェクトとソースコードはこちら(NetBeansで実行するには、test_lib以下に冒頭のfestライブラリをコピーする必要がある)。

Shift+F6を押してテストケースを実行してみると、下記のエラーとなる(折り返されて見づらいが、実際には1行で表示される)。

junit.framework.AssertionFailedError: [javax.swing.JCheckBox[name=null, text='チェックボックス', selected=false, enabled=true, visible=true, showing=true] - property:'selected'] expected:<true> but was:<false>

selected(true)を期待していたが、実際にはfalseになっている、というJUnitの報告が挙がっている。

そこで、起動初期状態として、チェックボックスが選択された状態にするコードを追加する。

    public NewJFrame() {
        initComponents();
        jCheckBox1.setSelected(true); // 追加
    }

NewJFrame.javaに上記の3行目を追加し、Ctrl+F6でテストを実行する。結果、JUnitは成功して終了する。[補足:本来は、この程度であれば上記のように手でコードを追加するより、Mattise上のプロパティ(下図)で設定を行った方が、手段の統一としては良いのかもしれない]

fest03

2つのチェックボックスの状態をチェックする

先ほどJUnitが成功したJFrameに、チェックボックスをもう1つ追加する(プロジェクト, ソースコード)。

fest11

ここで、先ほど成功したはずのテストケースを再度実行すると、失敗してしまう。

Found more than one component using matcher org.fest.swing.core.TypeMatcher[type=javax.swing.JCheckBox, requireShowing=true].

チェックボックスを探したところ、複数のコンポーネントがヒットしたのでどちらか特定できなくなっている。

これに対するfest側が想定している通常の対応は、コンポーネントに名前を付けておき、その名前で識別しよう、というものだ。

メインのコードで

    public NewJFrame() {
        initComponents();
        jCheckBox1.setSelected(true); // 追加
        jCheckBox1.setName("1個目"); // コンポーネントに名前を設定
        jCheckBox2.setName("2nd");
    }

setNameメソッドで名前をセットしておけば、テストコード上ではその名前で識別できるようになる。

    @Test
    public void testCheckBox() {
        window.checkBox("1個目").requireSelected(); // 引数に名前を指定
        window.checkBox("2nd").requireNotSelected();
    }

ただ、あらかじめこのことを知っていれば良いのだが、通常、名前をつけるようなことはあるのだろうか。私は今回初めてnameという属性があるのを知ったのだが...

nameの設定を行っていない場合、表示されている文字列(setTextで設定した文字列)で2つのチェックボックスを識別できれば良いのでは、と考えた。

今まで使用してきたcheckboxメソッドは、Javadocを見るとContainerFixtureで実装されていることが分かる。また、このメソッドはnameを引数にとるものの他、GenericTypeMatcherを引数にとるものがある。今回はこちらを用いる(ソースコード)。

    @Test
    public void testCheckBox() {
        GenericTypeMatcher<JCheckBox> matcher1 =
                new GenericTypeMatcher<JCheckBox>(JCheckBox.class) {

                    @Override
                    protected boolean isMatching(JCheckBox component) {
                        return "チェックボックス".equals(component.getText()) ? true : false;
                    }
                };
        GenericTypeMatcher<JCheckBox> matcher2 =
                new GenericTypeMatcher<JCheckBox>(JCheckBox.class) {

                    @Override
                    protected boolean isMatching(JCheckBox component) {
                        return "2nd check".equals(component.getText()) ? true : false;
                    }
                };

                window.checkBox(matcher1).requireSelected();
                window.checkBox(matcher2).requireNotSelected();
    }

上記のように、isMatchingの中でコンポーネントを識別できるような実装を行うことになる。

コンポーネントごとにmatcherを作る必要があるようなので、かなり面倒な作業になる。festを使用するのであればnameは設定しておくべき、ということなのだろう。

なお、表示文字列で識別する場合、多言語対応しているアプリケーションでは問題が出るであろうことが予想される。(自分で実装しない部分、たとえばOK/キャンセルダイアログのボタン等も実行環境によって文字列が自動で切り替わる。このようなものについても検討が必要かもしれないが、未検証。)

 

参考:

2009/02/11

NetBeansでcoverage reportを行う

  • できること
    • NetBeansでJUnitテストを実施(Alt+F6などで)する際、同時にカバレッジを測定する。
    • 結果をhtmlで出力する。
  • できないこと
    • IDEとの完全な統合。Eclipse+djUnitであれば、こんなふうに未実行のラインをタスク一覧に表示できたりするようだが、今回の方法では不可能。
    • プロジェクトごとにカバレッジ測定用のAntタスクを追記する必要がある。新規プロジェクトを作成し、このプロジェクトでもカバレッジ測定を行いたい場合は、同じような設定を追記しなければならない。
    • 前回採取したカバレッジと今回結果をマージして出力、といったこともできるはずだが、そこまで考慮できていない。
    • テストクラスを選択してSHIFT+F6を押せば、そのファイルだけJUnitを走らせることができるが、これが機能しなくなる(正常に実行できない)。

 

NetBeans上でカバレッジ測定を行えるものを探したところ、Unit Tests Code Coverage Pluginというものがあった。しかし、インストールしてみたところ、プロジェクト一覧のツリーで右クリックが行えなくなり(プラグイン内部でNullPointerExceptionが発生しているようだ)、私の環境では全く利用できなかった。

そこで、djUnitが内部で用いているjcoverage(GPL版)の後継であるCoberturaを、NetBeansのAntタスクで実行することにした。このときのWindowsでの設定手順を下記に示す。

 

環境変数ANT_HOMEの設定

マイコンピュータを右クリック>プロパティでシステムのプロパティダイアログを開き、詳細設定タブの環境変数ボタンを押す。

表れた環境変数ダイアログで、新規ボタンを押し、ユーザの環境変数を追加する。

変数名 ANT_HOME
変数値 <netbeans-install-root>/java2/ant

なお<netbeans-install-root>は、実際には下記のキャプチャのような、NetBeansをインストールしたディレクトリを指定する。

cobertura01 

既にNetBeansを起動しているのであれば、環境変数を有効にするために一旦終了させる必要がある。

 

Coberturaのインストール

Coberturaのダウンロードサイトから、バイナリをダウンロードして展開する。なお、現在の最新バージョンは1.9.1だったため、今回はこれを用いた。

展開後、cobertura.jar及びlib以下のasm-3.0.jar, asm-tree-3.0.jar, jakarta-oro-2.0.8.jar, log4j-1.2.9.jar(要するにjunit以外のjar)を、<netbeans-install-root>java2/ant/lib以下にコピーする。

 

以下は、カバレッジ測定するプロジェクトごとに設定が必要である。

project.propertiesの編集

NetBeansのファイル一覧ウィンドウから、対象プロジェクトの<project-name>/nbproject/project.propertiesファイルを開く。

cobertura02

最終行に、下記3行を追記する。

build.instrumented.dir=${build.dir}/instrumented
build.report.dir=${build.dir}/report
build.report.cobertura.dir=${build.report.dir}/cobertura

続いて、run.test.classpath設定箇所を探し、下記の通り書き換える。

run.test.classpath=\
    ${env.ANT_HOME}/lib/asm-3.0.jar:\
    ${env.ANT_HOME}/lib/asm-tree-3.0.jar:\
    ${env.ANT_HOME}/lib/cobertura.jar:\
    ${env.ANT_HOME}/lib/jakarta-oro-2.0.8.jar:\
    ${env.ANT_HOME}/lib/log4j-1.2.9.jar:\
    ${build.instrumented.dir}:\
    ${javac.test.classpath}:\
    ${build.test.classes.dir}

 

build.xmlの編集

<project-name>/build.xmlを開く。

最終行</project>の直前行に、以下を追記する。

<property environment="env"/>
<taskdef classpath="cobertura.jar" resource="tasks.properties"/>
<target name="-pre-compile-test">
    <delete dir="${build.instrumented.dir}" />
    <delete dir="${build.report.cobertura.dir}" />
    <mkdir dir="${build.instrumented.dir}" />
    <mkdir dir="${build.report.cobertura.dir}" />
    <cobertura-instrument todir="${build.instrumented.dir}">
        <fileset dir="${build.classes.dir}">
            <include name="**/*.class"/>
        </fileset>
    </cobertura-instrument>
</target>
<target name="-post-test-run">
  <!-- You can disable the html portion if you are using the hudson plugin and rely on the xml -->
    <cobertura-report format="html" srcdir="${src.dir}" destdir="${build.report.cobertura.dir}"/>
  <!-- Used by the hudson plug-in -->
    <cobertura-report format="xml" srcdir="${src.dir}" destdir="${build.report.cobertura.dir}"/>
    <delete file="cobertura.ser" />
</target>

 

以上の設定を行った上で、テストを実行すれば、build/report/cobertura/index.htmlにレポートが生成される。

 

参考:

2009/02/01

SwingのGUI Testingツールを調査した

JavaのGUIに関するテストを行うツールには、どのようなものがあるのか調べてみた。

  • Abbot Java GUI Test Framework - 画面操作をXMLで記録していく、java.awt.Robotを利用するタイプのもの。
  • FEST Swing - IBM developerWorks Japanで紹介されているTestNG-Abbotはプロジェクトが終了したようで、このプロジェクトが後継のようだ。ただし、FESTとAbbotには関連は無く、org.fest.swing.core.Robotという独自のインタフェースをimplementsすることでGUI操作を再生、記録するように見える[追記: Abbot由来のソースコードが含まれていることを確認]。全てJavaコードで記述する必要あり?
  • Marathon - 画面操作をJPythonあるいはJRubyで記録していく。Robotは用いていないようにみえる。テストはカスタム化されたJUnitで実行。
  • jfcUnit - XMLでイベントを記録する系統。昔から名前は聞いたことがある気がするが、長い間開発は止まっているようだ。
  • SwingUnit - 日本の方が開発されているようだ。
  • jemmy
  • Jacareto
  • Pounder
  • [追記]WindowTester Pro – Marathonと同じく、ユーザが操作した過程をイベントとして記録していく方式?Eclipse専用ツール?

その他、SWTのテストではEclipseのTPTPが利用できるらしい。商用プロダクトについては下記「参考」のtestingfaq.org参照。

low-levelなeventを記録できる、というのをメリットとして挙げているツールがいくつかあったが、実際に、これがメリットになるのかは不明だと思う。ウィンドウをどの座標に動かしただとかというような情報が必要になるテストケースは、実際のところ不要であることが大半なのではないかと思う。

Marathonでは、regular recodingというモードがデフォルトで、上記のような詳細なイベントを記録するにはraw recodingというモードが選択可能であるようだ。こちらの方が、記録されたイベントの取捨選択にかかるコストが少なくなるのでは無いかと思われる。

しかしながら、ユニットテスト的に用いるのか、受け入れテスト(acceptance test)的に使うのか等シチュエーションによって、適したツールは変わってくるのではないか。

 

参考:

« 2009年1月 | トップページ | 2009年3月 »

other sites

  • follow us in feedly
  • github
  • stackoverflow

ソフトウェアエンジニアとして影響を受けた書籍

  • Christain Bauer: HIBERNATE イン アクション

    Christain Bauer: HIBERNATE イン アクション
    理論と実践が双方とも素晴らしい製品であるHibernate。本書はそのプロダクトを書名に冠していますが、Hibernateを使うつもりがなく、ORマッピングの解説書として読むにしても十分な良書です。Second EditionとしてJava Persistence With Hibernateという書籍も出版されていますが、残念ながら現在のところ 和訳はされていません。-インアクションは2.xの、Java Persistence-は3.1の頃のものなので、最新版とはちょっと違うところもあることに注意。 (★★★★★)

  • アンドリュー・S・タネンバウム: 分散システム 原理とパラダイム 第2版

    アンドリュー・S・タネンバウム: 分散システム 原理とパラダイム 第2版
    クライアント/サーバシステムを構築する上で必要となる知識が総論されてます。Web技術者も、フレームワーク部分を開発するのであれば必読。 (★★★★★)

  • Joel Spolsky∥著: ジョエル・オン・ソフトウェア

    Joel Spolsky∥著: ジョエル・オン・ソフトウェア
    前述の書籍「ソフトウエア開発プロフェッショナル」をより砕いたもの、という感じでしょうか。 前書きではプログラマでなくSE向けの本のように書かれているが、プログラマが読んでも面白い本であると思われます。 SEになった新人(あるいはそういう会社に入る/入りたての人)にとっては、これからどういったことが仕事を遂行していく上で起こりえるのか、どのように考えて行なっていけばいいのか決定する助けになると思います。 元は″Joel on Software″というブログの記事で、web上でも一部日本語で読めます。 http://japanese.joelonsoftware.com/ (★★★)

  • ドナルド・C・ゴース,ジェラルド・M・ワインバーグ: ライト、ついてますか

    ドナルド・C・ゴース,ジェラルド・M・ワインバーグ: ライト、ついてますか
    問題解決(一昔前のの流行語で言うところの『ソリューション』)能力は、システムエンジニアのスキルとして備えるべきもののうちのひとつです。しかし、これは難しい。学校で出されるテストと違い、唯一の、(問題提出者が想定している)解を求めるだけが「問題解決」では無いからです。そもそも、何が問題なのか、それは本当に問題なのか、それは本当に解決すべき問題なのか、その問題解決方法は正しいのか、などを解決しなければ、「その解は正しいのか」に辿りつくことができません。この本の最も良いところのひとつは、本があまり厚くないこと。すぐに読めるし、何回も読み返す気になるでしょう。 (★★★★★)

  • スティーブ・マコネル: ソフトウエア開発プロフェッショナル

    スティーブ・マコネル: ソフトウエア開発プロフェッショナル
    コードコンプリートで有名なスティーブマコネルの著書。新人SEに読んで欲しい。個人として業界の中でどうあるべきか、組織としてどうあるべきか、SEのプロ意識とは?SEの心構え概論、といったところでしょうか。また、業界における資格の重要性についても説かれています。この業界では資格が特に軽んじられる傾向がありますが、この傾向はどんな弊害をもたらすのか、将来的にこの業界は資格に対してどのような姿勢で臨んでいくべきなのか。日経BP社では(他の出版社もだが)最近、似たような類いのあまり面白くない書籍が乱出版されていますが、この本は別格だと思うので安心して購入して欲しいと思います。 (★★★★★)

無料ブログはココログ