継続的インテグレーションを実践するメリットはいくつか考えられます。一番のメリットは環境依存のバグを発見できる点です。特にXPでは「ビルド専用マシン」を1台確保して、そのマシンで最新ソースのビルド、テストの実行、動作確認を行います。このことにより、OS、JDK、ライブラリ、環境変数などの違いによるバグを「早期に発見」することができます。「お客さんの環境で初めてバグが発見された」という最悪の事態を避けるためにも最終の実行環境に限りなく近いビルド用のマシンを用意しましょう。また、テストケースが1000を超えるような場合、すべてのテストを実行するにはかなりの時間がかかるようになってきます。そのような場合、自動テスト環境を構築してしまえば、テストのオーバーヘッドを最小限にすることができます。
今回は継続的インテグレーションのチュートリアルを用意しました。このチュートリアルでは自動的に1時間に1回、CVSより最新のソースを取得します。その後Antによるコンパイル・テストを行い、テストの結果をAntのJUnitReportタスクでHTMLに変換します。最終的にテスト結果をWebブラウザで確認できる環境を構築します。チュートリアルの環境は次のようになります(図1)。ビルド用の別のマシンを1台確保する点がポイントです。できれば、このビルドマシンは常時稼働できて、ビルド専用のマシンとして利用できるのが理想です。
図1 チュートリアルの環境
なお、CVS for NTはすでにセットアップ済みとします。まだ導入されていない方は本連載の第2回の「CVSとEclipseで『コードの共同所有』 」を参考にしてください。
図2 JUnitの結果をWebブラウザで確認(クリックすると拡大)
(1)チュートリアル用のプロジェクトの作成
[ファイル] - [新規作成] - [プロジェクト] - [Javaプロジェクト] - [次へ]
[プロジェクト名]に「Integration」と入力して[次へ]
「プロジェクトに含まれるソース・フォルダを使用」を選択して「新規フォルダの作成」
[新規ソースフォルダー]ダイアログが表示されるので相対パス「src」を入力して「OK」
「ビルド出力フォルダをIntegration/binにしますか」と聞いてくるので「はい」
「終了」
[ファイル] - [新規作成] - [フォルダー] - フォルダ名に「lib」と入力 - [終了]
以上の操作でチュートリアル用プロジェクトが作成されました。プロジェクトの構成は以下のようになります。
図3 プロジェクトの新規作成
(2)junit.jarをプロジェクトに追加
junit.jarをプロジェクトに追加します。次の方法でjunit.jarをインポートします。
[ファイル] - [インポート]
[ファイルシステム]を選択 - [次へ]
「ディレクトリ」の[ブラウズ]をクリックして「ECLIPSE_HOME/plugins/org.junit_3.7.0」を選択
ファイルの一覧が表示されるので「junit.jar」にチェックを入れる
[インポートするリソースの宛先の選択]のフォルダの「ブラウズ」をクリックして「Integration/lib」を選択
次のような状態になっていることを確認して[終了]
図4 junit.jarをプロジェクトに追加する(クリックすると拡大)
(3)junit.jarをクラスパスに追加
[プロジェクトで右クリック] - [プロパティ]
[Javaのビルドパス]を選択
[ライブラリ]タブを選択
[JARの追加]で(2)でインポートしたjunit.jarを選択 - [OK]
[OK]でプロパティダイアログを閉じる
図5junit.jarをクラスパスに追加する(クリックすると拡大)
(4)クラスとテストケースのソースを作成
テスト対象クラスとテストクラスを作成します。いつものStringUtilを例として使用します。
[ファイル] - [新規作成] - [クラス]で以下の2つのクラスを作成します。
package sample;
import java.util.StringTokenizer;
/**
* StringUtil
*/
public class StringUtil {
/**
* 文字列の分割(区切り文字指定)
* @param
str 対象文字列
* @param
delim 区切り文字列
* @return
分割後の文字列
*/
public static
String[] split(String str, String delim) {
StringTokenizer
st = new StringTokenizer(str, delim);
int
length = st.countTokens();
String[]
result = new String[length];
for
(int i = 0; i < length; i++) {
result[i]
= st.nextToken();
}
return
result;
}
}
パッケージ:sample
クラス名:StringUtil
スーパークラス:java.lang.Object
package sample;
import junit.framework.TestCase;
/**
*TestCaseを継承したStringUtilのテストケース
*/
public class StringUtilTest extends TestCase {
public
StringUtilTest(String arg0) {
super(arg0);
}
public
void testSplit() {
//
分割対象の文字
String
str = "a,b,c";
//
カンマで文字列を分割
String[]
result = StringUtil.split(str, ",");
//
以下結果を確認 -----------
//
resultはnullではないはず
assertNotNull(result);
//
//
配列の長さは3のはず
assertEquals(3,
result.length);
//
分割結果が配列に格納されているはず
assertEquals("a",
result[0]);
assertEquals("b",
result[1]);
assertEquals("c",
result[2]);
}
public
void testSplit2() {
String
str = null;
String
result[] = null;
//
別の文字列を指定
str
= "A*B*C";
result
= StringUtil.split(str, "*");
assertNotNull(result);
assertEquals(3,
result.length);
assertEquals("A",
result[0]);
assertEquals("B",
result[1]);
assertEquals("C",
result[2]);
//
長い区切り文字
str
= "123[SEP]456[SEP]789";
result
= StringUtil.split(str, "[SEP]");
assertNotNull(result);
assertEquals(3,
result.length);
assertEquals("123",
result[0]);
assertEquals("456",
result[1]);
assertEquals("789",
result[2]);
}
public
void testSplit3() {
//
分割対象の文字(空文字列あり)
String
str = "a,,";
//
カンマで文字列を分割
String[]
result = StringUtil.split(str, ",");
//
以下結果を確認 -----------
//
resultはnullではないはず
assertNotNull(result);
//
配列の長さは3のはず
assertEquals(3,
result.length);
//
分割結果が配列に格納されているはず
assertEquals("a",
result[0]);
assertEquals("",
result[1]);
assertEquals("",
result[2]);
}
}
パッケージ:sample
クラス名:StringUtilTest
スーパークラス:junit.framework.TestCase
(5)Antでビルドファイルを作成する
CVSからファイルの更新、コンパイル、テストの実行・テスト結果の出力を行うAntの定義ファイルを作成します。
[ファイル] - [新規作成] - [ファイル]
「Integration」プロジェクトのトップディレクトリを選択
ファイル名に「build.xml」と入力して[終了]
「build.xml」に以下の内容を記述する
<?xml
version="1.0" encoding="Shift_JIS"?>
<project name="Integration" default="javac"
basedir="./">
<!-- プロパティ
-->
<property
name="src.dir" value="./src"/>
<property
name="classes.dir" value="./bin"/>
<property
name="lib.dir" value="./lib"/>
<property
name="doc.dir" value="./doc"/>
<property
name="report.dir" value="${doc.dir}/reports"/>
<property
name="result.dir" value="${doc.dir}/result"/>
<!-- ライブラリ
-->
<path id="libs">
<fileset
dir="./lib">
<include
name="*"/>
</fileset>
</path>
<!-- 初期化
-->
<target name="init">
<mkdir
dir="${classes.dir}"/>
<mkdir
dir="${doc.dir}"/>
<mkdir
dir="${report.dir}"/>
</target>
<!-- コンパイル
-->
<target name="javac"
depends="init">
<javac
srcdir="${src.dir}"
destdir="${classes.dir}">
<classpath
refid="libs"/>
</javac>
</target>
<!-- テスト
-->
<target name="junit"
depends="javac">
<delete>
<fileset
dir="${report.dir}" includes="**/*" />
</delete>
<!--
テストを実行して結果をXMLファイルとして出力 -->
<junit
printsummary="yes" haltonfailure="no">
<classpath>
<pathelement
location="${classes.dir}"/>
<fileset
dir="./lib">
<include
name="*"/>
</fileset>
</classpath>
<formatter
type="xml"/>
<batchtest
fork="yes" todir="${report.dir}">
<fileset
dir="${src.dir}">
<include
name="**/*Test.java"/>
</fileset>
</batchtest>
</junit>
</target>
<!-- JUnitReport
-->
<target name="report"
depends="junit">
<!--
XMLファイルを基にHTML形式のレポートを出力 -->
<junitreport
todir="${report.dir}">
<fileset
dir="${report.dir}">
<include
name="TEST-*.xml"/>
</fileset>
<report
format="frames" todir="${result.dir}"/>
</junitreport>
<!--
タイムスタンプ作成 -->
<tstamp>
<format
property="NOW" pattern="yyyy-MM-dd HH:mm:dd"/>
</tstamp>
<!--
実行時間を知るためにHTMLの一部を現在の日付時刻に置換 -->
<replace
file="${result.dir}/overview-summary.html"
token="Unit
Test Results"
value="Unit
Test Results - ${NOW}"/>
</target>
<!-- CVS
update -->
<target name="cvs">
<cvs
command="update -P -d"/>
</target>
</project>
build.xml
(6)定期実行用のバッチファイルを作成する
ビルドマシンで実行するバッチファイルを作成しておきます。このバッチファイルをCronで定期的に実行することになります。
[ファイル] - [新規作成] - [ファイル]
「Integration」プロジェクトのトップディレクトリを選択
ファイル名に「build.bat」と入力して[終了]
「build.bat」に以下の内容を記述する
cd c:\project\Integration
……(ビルドマシンの環境に合わせます)
call ant cvs
call ant report
(7)プロジェクトをCVSに登録する
[プロジェクトを右クリック] - [チーム] - [プロジェクトの共用]
[既存のリポジトリーロケーションを使用]のリポジトリを選択 - [次へ]
[プロジェクト名をモジュール名として使用]をチェック - [次へ]
[終了]
[同期化ビュー]のトップディレクトリで[右クリック] - [コミット]
以上で準備は完了です。最終的なファイル構成は次のようになります。
図6 最終的なファイル構成
こういう話を聞いたことがあります。「いまやってるプロジェクト、どうも失敗しそうなんだ。本番環境のセキュリティが厳しくて、どうしてもうまくいかないんだ。失敗すると5000万円の赤字になるみたいで……」。どうやら、すべてが出来上がってから本番の環境で動かしたのですが、動かなかったそうです。それから彼には会っていませんが、どうなったか気になります。ビルドマシンはなるだけ本番のマシン環境に合わせておいた方がよいでしょう。適当なマシンがなければ購入を考える価値もあります。すべてが出来上がって動かなかったとき、いままでの苦労が水の泡どころか、信用もお金もなくなります。本番に近い環境で継続的インテグレーションをしていれば、このようなアクシデントも未然に防ぐことができるでしょう。
ではビルドマシンに移動して、継続的インテグレーション環境を構築しましょう。
(1)CVSクライアントのインストール
http://ccvs.cvshome.org/servlets/ProjectDownloadList よりWindows版の最新バイナリである「cvs-1.11.4.zip」をダウンロード
ダウンロードしたファイルを適当な場所に解凍。ここの例ではCドライブの直下に解凍した
解凍ファイルの「cvs-1.11.4.exe」を「cvs.exe」にリネーム
解凍したディレクトリにパスを通す。例えば、「C:\cvs-1.11.4」に解凍した場合、環境変数Pathに「C:\cvs-1.11.4」を追加する
コマンドプロンプトを立ち上げて「cvs」と入力してEnter。次のメッセージが表示されCVSコマンドが使用できることを確認する
>cvs[Enter]
Usage: cvs [cvs-options] command [command-options-and-arguments]
(中略)
(2)プロジェクトをチェックアウトする
コマンドプロンプトからCVSコマンドを使用して、チェックアウトしてみます。
作業用のディレクトリを作成する。ここでは「C:\project」を作業用ディレクトリとする
コマンドプロンプトより次のコマンドを実行して、プロジェクトをチェックアウトする
>mkdir c:\project
>cd c:\project
c:\project>set CVSROOT=:pserver:agata@mobster.jp/home/cvs/root
c:\project>cvs login
Logging in to :pserver:agata@mobster.jp:2401/home/cvs/root
CVS password:[パスワードを入力]
(CVSを初めて使用する場合、ここで「.passwdファイルが読み込めない」というエラーが表示されますが気にする必要はありません)
c:\project>cvs checkout Integration
cvs server: Updating Integration
U Integration/.classpath
U Integration/.project
U Integration/build.xml
……(中略)
U Integration/src/sample/StringUtilTest.java
「c:\project\Integration」以下にプロジェクトのファイルがチェックアウトされていることを確認します。
(3)Antのセットアップ
ビルドマシンにAntをセットアップします。すでにAntが導入済みの場合はこの項目は省くことができます。ただし、AntからJUnitタスクを利用するためにはJUnitがクラスパスに通っている必要がありますのでご注意ください。ANT_HOME/libにjunit.jarを置けば自動的にクラスパスに追加されます。
「http://ant.apache.org/ 」よりAntの最新版をダウンロードする。現在の最新版は「apache-ant-1.5.3-1-bin.zip」
ダウンロードしたファイルを解凍する。今回はCドライブの直下に解凍した
環境変数「ANT_HOME」「C:\apache-ant-1.5.3-1」を設定
環境変数「Path」に「%ANT_HOME%/bin」を追加
c:\project\Integration\lib\junit.jarをC:\apache-ant-1.5.3-1\libにコピー
コマンドプロンプトより次のコマンドを実行して、Antにパスが通っていることを確認する
>ant -version
Apache Ant version 1.5.3 compiled on April 16 2003
コマンドプロンプトから次のコマンドを実行。
>cd
c:\project\Integration
C:\project\Integration>ant cvs
Buildfile: build.xml
cvs:
[cvs] Using cvs passfile: C:\Documents
and Settings\agt\.cvspass
[cvs] ? doc
[cvs] ? bin/sample
[cvs] cvs server: Updating .
……(中略)
[cvs] cvs server: Updating src/sample
BUILD SUCCESSFUL
Total time: 2 seconds
コマンドプロンプトから次のコマンドを実行。
>cd c:\project\Integration
c:\project\Integration>ant report
Buildfile: build.xml
……(中略)
init:
javac:
junit:
report:
[junitreport] Using Xalan version: Xalan Java 2.2.D11
[junitreport] Transform time: 1016ms
BUILD SUCCESSFUL
Total time: 4 seconds
手動での実行ができるようになったところで、次は定期実行に挑戦します。定期的にある処理を実行するにはUNIXなら「Cron」、Windowsならば「タスク」があります。今回はMobCronというWindows用のCronを利用します。MobCronはJava+SWTで動作します。GUIから設定を行うことができるシンプルなCronプログラムです。
(1)MobCornのダウンロード
http://www.mobster.jp/wiki/index.jsp?pid=MobCron よりMobCronの最新版をダウンロードします。
(2)MobCornの解凍
ダウンロードしたZIPファイルを適当な場所に解凍します。
(3)MobCornの起動
MobCron.exeからMobCronを起動します。
(4)設定の追加
[一覧の上で右クリック] - [追加]
build.batが1時間に1回実行されるように設定する
図7 一覧の上で右クリック(クリックすると拡大)
図8 build.batが1時間に1回実行されるように設定する
(5)設定ファイルの保存
[ファイル] - [保存]
(6)手動で実行してみる
(4)で追加した一覧の行を[右クリック] - [実行]で実行します。「ログ」のタブでAntの出力を確認できます。JUnitReportが実行されれば成功です。
(7)定期実行の開始
ツールバーの「人間が走っているアイコン」で定期実行を開始します。MobCorn.exeのショートカットをスタートアップに入れておくと便利です。
最後にビルドマシンで稼働するTomcatを使用して、JUnitReportの結果をWebブラウザで閲覧できるようにしましょう。
ビルドマシンにTomcatをセットアップ
「CATALINA_HOME/conf/server.xml」に次の記述を追加
<Context path="/Integration"
docBase="c:\project\Integration"/>
Tomcatを起動
Webブラウザで次のアドレスよりJUnitReportの実行結果を確認する
「http://[ビルドマシンのIPアドレス]:8080/Integration/doc/result/index.html」
これでいつでも最新のテスト実行結果を確認することができますね。
Webのシステムの場合、ビルドマシン上に最新ソースによる実行環境を構築することも簡単に実現できます。この場合、ビルドマシンでコンパイルした結果をアプリケーションサーバに配備するAntタスクを実行します。
継続的インテグレーションの実践は環境依存のバグの低減につながります。また、自動化されたテスト実行環境は1度構築してしまえば、あとはビルドマシンが納品日までに何百回とテストを実行してくれます。「早めにリスクを取り除く」ために、ぜひ継続的インテグレーションを導入することをお勧めします。
本連載もいよいよ次回が最終回です。次回はコーディング標準と、いままで書ききれなかった点について補足したいと思います。お楽しみに!
この記事に対するご意見をお寄せください
managemail@atmarkit.co.jp
▼縣俊貴(あがた としたか)
メディアファイブ株式会社所属。XML,フレームワークを中心に開発業務に携わる。Javaのコミュニティー団体であるMobsterを主催。現在MonsterにてJavaベースのWikiシステム「MobWiki」を開発している。
▼橋本正徳(はしもと まさのり)
メディアファイブ株式会社所属。XML、フレームワーク等の開発業務に携わる。Javaのコミュニティー団体である「Mobster」を縣と共に発起、運営。現在mobsterにてバグトラッキングシステム「mobbug」等を開発している。「日本XPユーザーグループ関西支部 九州分科会 」にも参加。ちなみにこの記事自身もCVSでバージョン管理し、縣と橋本とで共同所有されて書かれている。
▼Project Mobster(ぷろじぇくと もぶすたー)
福岡県福岡市を中心にJava言語を研究追求し、その成果物をWeb上に公開していく団体です。年齢・スキル・会社などを超えてボーダーレスに活動しております。
Copyright © ITmedia, Inc. All Rights Reserved.