BlueJでのユニットテスト

バージョン 1.0
BlueJ バージョン 1.3.0 用

Michael KÖlling
Mærsk Institute
University of Southern Denmark

Copyright © M. KÖlling

邦訳:るんるん

このドキュメントは「Unit Testing in BlueJ」の邦訳です。英語は苦手なので誤訳があちこちにあると思いますが、とりあえず意味をつかめる程度には役立つはずです。
なお、実際のBlueJでの表示ラベルの邦訳を確認していないので、微妙に表現の違うところがあります。 (2005/07/24)

  1. はじめに
  2. ユニットテスト機能を使用可能にする
  3. テストクラスの生成
  4. テストメソッドの生成
  5. テストの実行
  6. テスト結果の解釈
  7. フィクスチャとは何?
  8. テストフィクスチャの作成と使用
  9. 手動によるテストメソッドの記載
  10. 最初のテストの記述
  11. 多重クラスのテスト
  12. 要約

1 はじめに

要約: BlueJは、JUnitを統合することでリグレッションテスト (回帰テスト、退行テスト) 機能を提供します。

1.1 このチュートリアルについて - 範囲と読者

このチュートリアルはBlueJ環境において機能をテストするユニットを紹介します。 私たちは、あなたが既にBlueJの一般的な機能に詳しいと考えています。 そうでなければ、最初に「BlueJ Tutorial」を読んでください。 (あなたはそのチュートリアル、およびその電子版を http://www.bluej.org/doc/documentation.html で得ることができます)。 また私たちは、あなたがテストユニット (または、少なくとも一般的なソフトウェアテスト) の考えにいくらか詳しいと思っています。 私たちは次のセクションでいくつかのポインタを与えます。

1.2 ユニットテストとは何か?

ユニットテストという用語は、ソフトウェアシステムの別々のユニットの個々のテストのことを指します。 オブジェクト指向システムでは、これらのユニットは通常、クラスとメソッドです。 したがって私たちの文脈においてユニットテストとは、BlueJのメソッドとクラスの個々のテストについて言及するものです。

このチュートリアルは系統だったユニットテストのためのBlueJのツールについて議論します。 BlueJの双方向作用の特徴になじみ深いなら、あなたは各メソッドを対話的にテストすることがBlueJでは簡単であると知っています。 私たちはアドホックな (一時的な、その場限りの) テストであるそのことについて説明します。

アドホックなテストは有益ですが、系統だったテストにおいては十分とは言えません。 BlueJのユニットテスト機能は、後で (通常、システムの変更後に) 容易にユニットテストを繰り返すことができるよう、また開発者が最近の変更で壊れてしまった既存の機能性がないという何らかの信用を獲得することができるよう、テストを記録してリプレイするためのツールをあなたに与えます。 これはリグレッションテストとして知られています。

ユニットテストとリグレッションテストの概念は古いのですが、エクストリームプログラミング方法論1とJavaのためのユニットテストツールの発表により、それらの人気は最近とても高まりました。

JUnitは、Erich Gamma と Kent Beck によって書かれたリグレッションテストのフレームワークです。 あなたは http://www.junit.org でそれに関するソフトウェアと多くの情報を見つけることができます。

1 エクストリームプログラミングが何かを調べるには、例えば「Extreme Programming Explained: Embrace Change」(Kent Beck, Addison Wesley, 1999) を見てください。 利用可能な他の多くの本があります。 オンラインでは良い概要が http://www.xprogramming.com/xpmag/whatisxp.htm にあります。

1.3 BlueJでのユニットテスト

BlueJの系統的なテストツールはJUnitに基づいています。 したがって、JUnitの使用に関する何らかの知識が、BlueJにおけるユニットテストの理解を助けます。 私たちは、そのことに関する記事を読むのを (今すぐというわけではなく、少し後で) 勧めます。 そのような多くの記事が存在しており、JUnitのウェブサイトはそれらを見つける良い出発点です。

BlueJでのユニットテストは、BlueJの対話的なテスト機能とJUnitのリグレッションテストを組み合わせたものです。 双方のテスト方法が完全にサポートされます。 さらに、ふたつのシステムの組み合わせから生じる新しい機能も利用可能です: たとえば後でリグレッションテストを行なうためのJUnitテストメソッドを自動生成するために、対話的なテストシーケンスを記録することができます。 このドキュメントで後ほど例を挙げます。

BlueJのユニットテスト機能は、博士号の作業の一部として、Andrew Patterson (Monash University) によって設計および実装されました。

2 ユニットテスト機能を使用可能にする

要約:設定のスイッチで、テストツールを表示できます。

BlueJでの明示的なテスト支援は、最初は無効にされています。 テストツールを使用するには、ツール - 設定を選択し、テストツールの表示とのラベルのあるチェックボックスを選択してください。

この機能を使えるようにすると、3つの要素がインタフェースに追加されます: メインウィンドウではいくつかのボタンとのツールバーの「記録」インジケータ、表示メニューではテスト結果の表示という項目、そしてコンパイル済みクラスのポップアップメニューではテストクラスの生成という項目。

3 テストクラスの生成

要約:クラスのポップアップメニューからテストクラスの生成を選択することで、テストクラスを生成します。

BlueJのクラスやメソッドのテストのセットアップへの第一歩は、テストクラスを生成することです。

テストクラスは、私たちが参照クラスと呼ぶプロジェクトのクラスに、関連づけられているクラスです。 そのテストクラスは、参照クラスのメソッドのためのテストを含んでいます。

このチュートリアルでは例として、peopleプロジェクトを使います。それはBlueJとともに配布されている、examplesディレクトリの中のサンプルのひとつです。 あなたはこれを読んでいるとき、記載を十分に試すために自分のシステム上でそれを開きたいでしょう。

コンパイル済みクラスを右クリック (MacOS: コントロールクリック) し、ポップアップメニューのテストクラスの生成を選択することで、テストクラスを生成することができます。 このテストクラスは、参照クラスの名前に「Test」という接尾語を付加する形で自動的に命名されます。 たとえば参照クラス名が「Student」なら、テストクラスは「StudentTest」と命名されるでしょう。

テストクラス群は、<<unit test>>タグでマーキングされ、参照クラスに張りつく形でダイヤグラムに表示されます。 またそれらは異なった色 (図1) になります。 参照クラスをドラッグしても、テストクラスは参照クラスに張りついたままです。

図1: 関連するテストクラスのある参照クラス

テストクラス群は、開発環境によって特殊な方法で扱われます。 これらには (エディタの表示, コンパイル, 削除などの) 普通のクラスの機能がありますが、いくつかテストに特化した機能 (図2) もあります。 このメニューを見るには、テストクラスをコンパイルしなければなりません。

図2: テストクラスのポップアップメニュー

テストクラスの生成においては何もテストは作成されませんが、テスト作成にはオプションがあります。 テストクラスは、私たちが作成するだろうテストを保持するために使用されます。

4 テストメソッドの生成

要約:テストクラスのメニューから、テストメソッドの生成...を選ぶことで、テストメソッドを生成します。

Studentオブジェクトには二つのメソッドがあります。それは (Personから継承した) setNameおよびgetNameで、学生の名前を設定/取得するものです。 これらのメソッドが予想通りに動くことをチェックするために、テストを生成したいと考えていると仮定してください。

私たちは、StudentTestクラスからテストメソッドの生成...を選択することで始めます。 testメソッドは、(以下の、機能のうちごくわずかなテストをする) ただ一つのテストを実装します。

この機能を選択した後、あなたはこのテストを命名するよう求められます。 テスト名は常に、接頭語「test」で始まります - あなたの選んだ名前が「test」から始まらないと、この接頭語が自動的に付加されるでしょう。 したがって「testName」か「name」とタイプすると、ともに「testName」と呼ばれるテストメソッドが作成されることになります。

名前をタイプしてOKをクリックした後、すべての対話が、このテストの一部として記録されます。 「記録」インジケータはオンであり、このテストの記録を終了またはキャンセルするためのボタンが使用可能になります (図3)。

図3: テスト記録中のテストボタン

この例のようにテストを記録するには、次のようにします:

getNameメソッドを呼んだ後、結果ダイアログが表示されます。 私たちがテストを記録している間、結果ダイアログには、明示的にアサーションを指定することのできる部分が含まれます。 私たちはテストの予想される結果を指定するのに、このアサーション機能を使うことができます。 この例の場合では、メソッド呼び出しの結果が文字列「Fred」と等しいと予想し、アサーションとしてそれを指定することができます (図4)。

図4: アサーションオプションがついたメソッドの戻り値ダイアログ

いくつかの異なるアサーションが、ポップアップメニューから利用可能です。それらは、等しいか、nullか、nullではないか、というテストを含んでいます。

これが私たちのテストケースを結論づけるので、テストの記録を終了するために、テスト記録インジケータの下にある「終了」ボタンをクリックします。 テストが終わると、テストクラスにテストメソッドが追加されます。 このテストメソッドはただちに実行することができます。

記録を終了して破棄するには、記録「取り消し」ボタンを使います。

この例と同様の方法で、私たちはいろいろなテストを記録することができます。 プロジェクト内の各クラスは、それ自身のテストクラスを持つことができ、各テストクラスはいろいろなテストを持つことができます。

各テストの記録は、インスタンスの任意の生成と複数のアサーションを含む、任意の数のアクションで成り立ちます。

5 テストの実行

要約:テスト実行ボタンをクリックすることで、すべてのテストを実行します。 テストクラスのメニューからそれらを選択すると、個別にテストを実行します。

テストがいったん記録されると、それらを実行することができます。 テストクラスは参照クラスと同じくJavaのクラスなので、実行の前にそれらもコンパイルされなければなりません。

BlueJは各テストの記録後、テストクラスを自動的にコンパイルしようとします。 もしアサーション式に誤りがあるか、あるいはテストクラスが手で編集された場合は、使用する前にテストクラスを明示的にコンパイルする必要のある場合があります。

今、私たちがテストクラスを右クリックすると、そのクラスのポップアップメニューの中に、記録されたばかりのテストを見つけることができます。 図5は、上からtestNameテストメソッド、次にtestStudentIDを呼ぶテストの例です。

図5: 二つのテストメソッドが定義された、テストクラスのメニュー

メニューからテストを選択すると、そのテストは個別に実行されます。 テストクラスのメニューからすべてのテストオプションを選択すると、そのクラスで定義されたすべてのテストが実行されます。

テストが個別に実行される場合、ふたつの事態のうちひとつが起きるでしょう: テストが成功した (テストにおけるアサーションが成立した) なら、ウインドウ下部の、プロジェクトウインドウのステータスバーに、成功を示す短信が示されます。 テストが失敗した (アサーションが失敗したか他の問題が起きた) なら、その詳細を提示するテスト結果ウインドウが表示されます。

すべてのテストを実行する場合、その結果を示すため、テスト結果ウィンドウが常に表示されます。

また、メインウインドウのテスト記録インジケータの上にあるテストを実行ボタンを使うことができます。 このボタンをアクティブにすると、そのパッケージの全テストクラスの全テストが実行されます。 これはパッケージのための完全な統合テストを実行する標準的な方法です。

6 テスト結果の解釈

要約: テスト結果ウインドウは、テスト実行の要約と失敗内容の詳細を表示します。

図6: テスト結果ウィンドウ

テストが実行されると、テスト結果ウインドウはテスト結果(図6)に関する概要を表示します。 ウインドウの一番上のペインは、すべての実行テストの一覧を表示し、その成功/失敗はアイコンで示されます。 緑色のチェックマークは成功したテストを示し、灰色の×印は失敗したテストを示し、赤い×印はエラーを示します。

実行したテスト、エラー、そして失敗の数は、ウインドウの中央部分に略記されます。

アサーションの一つが成立しない場合、テストには失敗 (灰色の×印) があります。 たとえばテストアサーションが、特定のメソッドの結果がnullであるべきではないとするかもしれませんが、この場合は失敗しました。

予期されない例外が投げられたといったように、実行が他の種類のエラーをもたらしたなら、テストにはエラーがあります。

どのようなテストの失敗においても、リストにあるテストを選択することで、失敗に関する詳細を表示できます。 ウインドウ下部のペインは、この失敗もしくはエラーに関する詳細な情報を表示します。

テストウインドウの中央のバーは、テスト実行の主な概要です: 緑色ならすべて順調です - すべてのテストが成功しました。もし赤なら問題があります - 少なくともひとつのテストが失敗しました。

MacOSでは、このバーの色が変わらないことに注意してください。

7 フィクスチャとは何?

要約: テストフィクスチャは、テストの出発点として使用されるよう準備されたオブジェクトセットです。

時々テストは、実際に開始できるようになる前に、いくつかのオブジェクトのセットアップを必要とすることがあります。 たとえば「people」プロジェクトのDatabaseクラスのテストには、Databaseオブジェクト, Studentオブジェクト、そしてStaffオブジェクトが必要です。 さらに、学生と職員の特定の状態 (名前と年齢のセットなど) がほしいと思うかもしれません。

私たちはテストを行なうため、必要なオブジェクトを作成し、それらに適切な状態を設定することによって、すべての個別テストを始められます。 しかしテストがより洗練されるにつれ、これは退屈になります。 私たちはこのオーバーヘッドを避けるために、よりよいメカニズムを使用することができます。

使いたいと思う特定のテストクラスのすべてのテストの出発点として、(それぞれある状態のオブジェクトのセットである) 状態をオブジェクトベンチで生成することができます。 この開始用のオブジェクトセットはフィクスチャと呼ばれます。

フィクスチャは定義することができます。そして同じテストクラスのあらゆるテストの最初に、自動的にセットアップされます。その結果、各テストのオーバーヘッドを下げます。

8 テストフィクスチャの作成と使用

要約: テストクラスのフィクスチャを作成するには、必要なオブジェクトをオブジェクトベンチに作成し、次にテストクラスのメニューから Object Bench To Test Fixture を選択します。

私たちは単に必要なオブジェクトを作成し、そしてそのオブジェクトを望む状態に設定するメソッドを作ることで、テストフィクスチャの作成を始めます。

たとえばpeopleプロジェクトのデータベースクラスをテストするために、私たちはDatabaseオブジェクトや、そのデータベースにある"Fred"という名前のStudentオブジェクト、そのデータベースにない"Jane"という名前のStaffオブジェクトがほしいかもしれません。 私たちはただオブジェクトを生成し、データベースにFredを挿入するという必要な呼び出しを作ることで、始めることができます。

いったんオブジェクトベンチが私たちのテストを始められる状態になれば、私たちはDatabaseTestクラスから、Object Bench To Test Fixture 機能を選択できるようになります。

この機能を選択すると、ベンチからすべてのオブジェクトが削除している間に、そのクラスのためのテストフィクスチャが作成されます。

クラスにフィクスチャがあると、このフィクスチャはあらゆるテストの最初に再作成されます。 たとえば (そのテストクラスから Create Test Method を選択することで) Databaseクラスのための新しいテストを作成すると、フィクスチャで定義された状況が自動的に復元されます。 フィクスチャオブジェクトはテスト記録の開始時に、定義された通りの状態でベンチ上に現われます。

またテストクラスのメニューから Test Fixture To Object Bench を選択することで、テストフィクスチャの状態を明示的に再作成することができます。 新しいテストメソッドはフィクスチャオブジェクトの追加を必要とするため、これはあとでフィクスチャを拡張したいときに役立ちます。

その場合、私たちはフィクスチャを再生成するのに Test Fixture To Object Bench を使用し、そして手動で、望む状態のフィクスチャを追加します。 それがいったん終われば、私たちはフィクスチャを復元するのに、Object Bench To Test Fixture を再び選ぶことができます。 古いフィクスチャは置き換えられるでしょう。

9 手動によるテストメソッドの記載

要約: テストクラスのソースコードに、直接テストメソッドを書くことができます。

対話とオブジェクトベンチの状態の記録によるテストメソッドやフィクスチャの生成は、ユニットテストを生成するオプションのひとつにすぎません。 他方のオプションは、手動でテストメソッドを書くことです。

テストクラス群は、プロジェクトの他のクラスと同じくJavaのクラスであり、同じように扱うことができます。 私たちはエディタを開いてソースコードを見ることができ、コードを編集してコンパイル、実行することができます。

JUnitの伝統的な (非BlueJの) 使用、これはテストメソッドを作成する標準的なものですが、BlueJは同じスタイルの扱いを許します。 テストを対話的に記録すると、手動によるテスト記載への追加ではなく、交換になります。

JUnitになじみのない人々のためには、テストクラスのソースコードを調べるために、テストフィクスチャといくつかのテストメソッドを対話的に生成することから始めるのは良い方法です。 私たちは各テストクラスに、テストフィクスチャをセットアップするのに使用される setUp() というメソッドがあるのに気づくでしょう。 また、各テストあたり一つの追加メソッドがあります。

そのふるまいを変更するため、あるいは完全に新しい手書きメソッドを追加するために、既にあるメソッドを編集するのはすばらしいことです。 テストメソッドとして認識されるためには、テストメソッドの名前が、接頭語「test」で始まらなければならないことを忘れないでください。

JUnitテストを書くことについてもっと知りたい人は、このチュートリアルの末尾にあるJUnitの参考文献をいくつか読むべきです。

10 最初のテストの記述

要約: 実装の前にテストを作成するには、テストを手書きするか、メソッドスタブを使うことができます。

エクストリームプログラミング方法論 [ref] は、どのようなメソッドも実装の前にテストが書かれるべきであると示唆しています。 BlueJの統合されたユニットテストを使えば、これをふたつの異なった方法で行なうことができます。

第一に、前のセクションで説明されたようにテストを手書きすることができます。 テストを書けば、非BlueJのJUnitの実装と同じように動作します。

第二に私たちは、非voidの返値を持つメソッドのためにダミー値を返す、参照クラスのメソッドスタブを作成できます。 これを行なったあとで、私たちは対話的な記録機能を使ってテストを作成し、実装終了に向けた期待に添うアサーションを書くことができます。

11 多重クラスのテスト

要約: 連結していないテストクラス群を生成するのに、新規クラス 機能で Unit Test 型のクラスを使うことができます。

この例は、参照クラスと連結していないテストクラス群を使います。 連結は、テストで他のクラスタイプのテストクラスを使うのを防ぎませんが、それは参照クラスへのテストクラスの論理的な接続を示します。

テストクラスは時々、いくつかのテストクラスの組み合わせとして書かれます。 これらのクラスは論理的に1つのクラスのものではありません。 これを書くために、直接それらを1つのクラスに連結するべきではありません。

通常の 新規クラス 機能を使用し、新規クラス作成ダイアログで Unit Test を選択することで、連結されないテストクラス群を作成できます。

他のテストクラス群と同じように、連結されていないテストクラス群を使用することができます。 私たちはテストフィクスチャを作成し、テストメソッドを作ってテストを実行することができます。

12 要約

  1. BlueJは、JUnitを統合することでリグレッションテスト (回帰テスト、退行テスト) 機能を提供します。
  2. 設定のスイッチで、テストツールを表示できます。
  3. クラスのポップアップメニューからテストクラスの生成を選択することで、テストクラスを生成します。
  4. テストクラスのメニューから、テストメソッドの生成...を選ぶことで、テストメソッドを生成します。
  5. テスト実行ボタンをクリックすることで、すべてのテストを実行します。 テストクラスのメニューからそれらを選択すると、個別にテストを実行します。
  6. テスト結果ウインドウは、テスト実行の要約と失敗内容の詳細を表示します。
  7. テストフィクスチャは、テストの出発点として使用されるよう準備されたオブジェクトセットです。
  8. テストクラスのフィクスチャを作成するには、必要なオブジェクトをオブジェクトベンチに作成し、次にテストクラスのメニューから Object Bench To Test Fixture を選択します。
  9. テストクラスのソースコードに、直接テストメソッドを書くことができます。
  10. 実装の前にテストを作成するには、テストを手書きするか、メソッドスタブを使うことができます。
  11. 連結していないテストクラス群を生成するのに、新規クラス 機能で Unit Test 型のクラスを使うことができます。