Eclipse CDTを使ったMacでのPCUnitの環境構築

 引き続きTDDBC大阪向けの資料です。
 TDDBC大阪ではCのテスティングフレームワークにPCUnitが選択されそうなので、今回はPCUnitの概要と、Mac OS XでのEclipse CDT上のPCUnitの環境構築方法をまとめます。なおPCUnitはRubyスクリプトによるテストコード自動生成が大変便利なので、今回はスクリプト実行環境も合わせて構築しています。
 ちなみにPCUnitは日本人の @ さんが開発したものであるため、既に分かりやすい日本語ドキュメントが「katono/PCUnit · GitHub」等で公開されています。入門の際は本エントリよりまずそちらを当たった方が無難だと思います。


PCUnitについて

  • 組み込み開発も対象に含むC/C++向けユニットテスティングフレームワーク
  • サイト: https://github.com/katono/PCUnit
  • シンプルなフレームワーク。標準C89規格のファイルでコアを構成。マクロで標準ライブラリへの依存も取り除ける。Rubyスクリプトで機能拡張。そのためマルチプラットフォーム対応で、色々な開発環境(例えばHEW等も)に組み込める。
  • ホスト/ターゲット両方で実行可能。組み込みのプアなターゲット環境でも動作する。
  • CUnitに似た構文や命名、マクロ。ただ命名や実行フローはCUnitよりもよりxUnitファミリーの慣習に準拠している。
  • Rubyスクリプトを使ったテストランナー自動生成をサポート。テストメソッドを書いたら自動的に見つけ出して実行してくれる。そのためTDD向き。
  • 再利用しやすい緩いライセンスで、カスタマイズも容易。

手順

1. Rubyのインストール

 Rubyがインストールされていなければインストールします。

2. Eclipse CDTの環境構築

 「http://d.hatena.ne.jp/goyoki/20120526/1338055942」での手順1.、2.を実行し、C言語がビルド可能なEclipse CDTの環境を確保します。

3. PCUnitのダウンロード

 開発者のgithub「https://github.com/katono/PCUnit」から、最新版のZIPファイルをダウンロードし解凍します。

4. PCUnitのインストール

 Macのターミナルで、解凍したZIPファイルの中のPCUnitディレクトリに移動し、「make install」コマンドを実行します。

5. テストコードを書くプロジェクトの作成

 2.で構築したEclipseで、適宜の場所でテストコードを書くプロジェクトを作成します。プロジェクトはとりあえず「C Project」の「Hello World ANSI C Project」にします。またToolchainsは「MacOSX GCC」を選択します。

6. ライブラリおよびRubyスクリプトの登録

 EclipseのProject Properties(Project ExplorerでProjectを右クリック→Properties)にて、以下の設定を行います。

  • 「Tool Settings」タブのMacOS X C++ LinkerのLibrariesにて、
    • Libraries(-l)に「pcunit」を追加
  • 「Build Steps」タブの一番上の「Command:」にて、
    • 「/usr/local/bin/pcunit_register.rb -d [テストコードを置くディレクトリパス]」を入力
7. スケルトンコードの生成

 Macのターミナルでテストコードを置くディレクトリに移動し、「pcunit_temlate.rb -m」を実行。これによりTest Runnerであるmain関数が生成されます。なお自分は結果をカラフルに表示したいので、以下のように「 PCU_enable_color()」を生成コードに追記しています。

int main(void)
{
        const PCU_SuiteMethod suites[] = {
        };
        PCU_set_putchar(putchar);
        PCU_set_getchar(getchar);
        PCU_enable_color();
        PCU_run(suites, sizeof suites / sizeof suites[0]);
        return 0;
}
8. テストコードの実装

 テストコードを書く.cファイルを適宜作成し、以下のようなコードを書きます。コード中の「(テストスイート名)」はHogeTestなど、適当な名前に置換してください。
 

#include <PCUnit/PCUnit.h>

static void test_hoge(void)
{
     PCU_ASSERT_EQUAL(3, 3);
}

PCU_Suite *(テストスイート名)_suite(void)
{
	static PCU_Test tests[] = {
	};
	static PCU_Suite suite = {
		"(テストスイート名)", tests, sizeof tests / sizeof tests[0], NULL, NULL, NULL, NULL
	};
	return &suite;
}
9. ビルドと実行

 実行すると、「test_hoge()」が自動で「PCU_Test tests[] 」に追加され、さらに「(テストスイート名)_suite(void)」がmain()関数に登録されます。そして「test_hoge()」がテストコードとして実行されます。
 あとは「static void test_適当なテストメソッド名(void)」のような関数を継ぎ足していけば、実行の度にその関数が自動でmain()に登録され実行されます。

 新たなテストコードのファイルを作る際は、以下のようなコードをファイル末尾に追記します。なおこのコードを含むテストスイートのスケルトンは、スケルトンを生成したいディレクトリで「pcunit_template.rb テストスイート名 -m」を実行しても生成できます。

PCU_Suite *(テストスイート名)_suite(void)
{
	static PCU_Test tests[] = {
	};
	static PCU_Suite suite = {
		"(テストスイート名)", tests, sizeof tests / sizeof tests[0], NULL, NULL, NULL, NULL
	};
	return &suite;
}

Eclipse CDTを使ったMacでのgoogle testの環境構築

 6月はじめのTDDBC大阪向けの資料です(6/2、6/3にて大阪でTDD Boot Camp大阪<http://atnd.org/events/28762>が開催され、自分はそこでTAを担当する予定です。参加キャンセル等で参加できる余地もあるので、興味ある方はぜひ)。
 TDDBC大阪では、C++のテスティングフレームワークgoogle testが選択されそうなので、日本語資料が少なげなMac×Eclipse CDTでの環境構築方法を、今回参考情報としてまとめます。

Eclipse CDTを使ったMacでのgoogle testの環境構築

1. XCodeのインストール

 コンパイラ等を確保するため、App Store等でXCodeをインストールします。

2. EclipseとCDTをインストール

 公式サイト等でEclipseとCDTをダウンロードし、使用可能にしておきます。初心者の方はEclipse、CDT、日本語化がセットになったPleiadesが無難かもしれません。

3. google testのダウンロード

 公式サイトから最新版のgoogle testをダウンロードします。

4. gtest_mainのビルド

 Macのターミナルで、google testを解凍したディレクトリにあるmakeディレクトリに移動します。そこで「make」コマンドを実行して、google testのライブラリファイル「gtest_main.a」をビルドします。そしてその「gtest_main.a」を「libgtest_main.a」とリネームしておきます。

5. テスト対象やテストを書くプロジェクトの作成

 2.で構築したEclipseにて、適宜の場所でテストコードを書くプロジェクトを作成します。プロジェクトはとりあえず「C++ Project」の「Hello World C++ Project」にします。またToolchainsは「MacOSX GCC」を選択します。

6. ライブラリとヘッダの確保

 5.で作成したプロジェクトディレクトリに、「lib」ディレクトリを新規作成。そこに4でビルド&リネームした「libgtest_main.a」を置きます。
 また同じくプロジェクトディレクトリに、google testを解凍したディレクトリにある「include」ディレクトリをコピペします。

7. ライブラリのリンクとインクルードパスの設定

 EclipseのProject Properties(Project ExplorerでProjectを右クリック→Properties)にて、以下の設定を行います。

  • MacOS X C++ LinkerのLibrariesにて、
    • Libraries(-l)に「gtest_main」を追加
    • Library search pash (-L)に「"${workspace_loc:/${ProjName}/lib}"」を追加(あるいは6.で作成した「lib」ディレクトリを手動追加)
  • GCC C++ Compilerにて、
    • IncludesのInclude paths (-l)に「"${workspace_loc:/${ProjName}/include}"」を追加(あるいは6.でコピペした「include」ディレクトリを手動追加)
8. 実行

 あとはHello Worldのひな形となっているmain()関数を、以下のようなコードで上書きすれば、google testが実行されます。

#include <gtest/gtest.h>

using namespace std;

int main(int argc, char **argv)
{
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

C言語向けユニットテスティングフレームワーク Unityについて

 C言語ユニットテストフレームワークには、昔はCUnit+自前のテストコードジェネレータを使用していたのですが、前々からUnityの使用機会が増えています。
 理由は色々ありますが、自分の場合だと以下の点で便利なのが特に大きいです。

  • 移植性に優れるため組み込み環境(ホスト、ターゲット両方で)でも使える
  • 標準で自動テストディスカバリ機能(テストランナーの自動生成)を持っているので、そのままTDDのような軽快なテスティングを実現できる

 特に組み込みCでTDDをやる場合は、PCUnitと並ぶ有望なテスティングフレームワークだと感じます。
 少し前にTest Driven Development for Embedded Cの読書会にて解説を行う機会もあったので、今回Unityについて簡単にまとめたいと思います。

特徴

  • 組み込み開発も対象に含むC言語向けユニットテスティングフレームワーク
  • サイト: Unity - Compact Test Framework for C
  • シンプルなフレームワーク。標準ANCI C準拠の3つのファイルでコアを構成。rubyスクリプトで機能を拡張する。そのためマルチプラットフォーム。組み込みのプアなターゲット環境でも動作する。
  • rubyを使った現代的な拡張機能を持つ。例えばCppUTestとの互換や、CMock等をサポート。
  • xUnitxUnit Test Patternsに従った構成や命名、実行フロー。CUnitよりこちらの方がxUnitファミリーとしてよりふさわしい。
  • rubyスクリプトを使ったテストランナー自動生成をサポート。テストメソッドを書いたら自動的に見つけ出して実行してくれる。

 

基本構成

 テストメソッドは以下のような感じです。Assertionメソッドは「TEST_ASSERT_EQUAL」などを使います。「TEST()」がテストメソッドです。

#include "unity.h"

TEST(TestSuiteName, TestCaseMethodName)
{
    TEST_ASSERT_EQUAL(3, TestTarget());
}


 Four Phase Testもサポートしています。

#include "unity.h"

TEST_GROUP(TestSuiteName);

//setup
TEST_SETUP(TestSuiteName)
{
...
}
//teardown
TEST_TEAR_DOWN(TestSuiteName)
{
...
}


 なおテストランナーを手動で実装する場合は以下のようなコードが必要になります。面倒ですが、CUnitに比べればシンプルです。

//Test Case Methodを実行
//SetupとTeardownは定義されていれば各Test Case Methodごとに実行されます
TEST_GROUP_RUNNER(TestSuiteName)
{
    RUN_TEST_CASE(TestSuiteName, TestCaseMethodName);
    …(TestSuiteNameに属する他のTestCaseMethodを同じように羅列)…
}
//Test Suiteを実行
static void RunAllTests(void) 
{
    RUN_TEST_GROUP(TestSuiteName);
    …(他のTestSuiteを同じように羅列)…
}

//スタートアップ
int main(int argc, char * argv[]) 
{
    return UnityMain(argc, argv, RunAllTests); 
}

 

Rubyスクリプトによるテストコード生成&実行

 UnityはRubyスクリプトによる実行が大変便利です。とりあえずTest Runnerの自動実行についてまとめます。

 
 まずRubyスクリプトのサポートを得るには、RubyとRakeをインストールして実行可能にしておきます。
 次にスクリプトの実行環境を構成します。スクリプトの実行にはHelperコードやymlファイル、rbファイルなどが必要です。それらに関しては、Unityが使い勝手の良いサンプルを十分に用意しているので、それを流用して環境を構成すると無難に確保できます。(なおサンプルの環境は、クロス開発でのユニットテストに対する、Unity開発者達のアプローチや戦略を学べる良い教材でもあります)
 とりあえずUnityのダウンロードファイルを解凍すると、「docs」「src」「build」などのフォルダがトップに展開されます。そのうち以下のフォルダをテスト環境を構築したいフォルダに丸ごとコピーします。

  • auto
  • example
  • src
  • targets

 このうち、autoに機能拡張のためのRubyスクリプトが、targetsに開発環境ごとの設定をまとめたymlファイルが、srcにUnity本体が置いてあります。またユーザはexampleにテストコードを書いていきます。
 exampleフォルダはテストプロジェクトフォルダでもあるので、適宜名前を変えましょう。exampleフォルダには以下があります。

  • build
  • helper
  • test
  • makefile
  • rakefile_helper.rb
  • rakefile.rb
  • readme.txt

 このうち、testにテストコードを格納します。srcにテスト対象を置いていますが、適宜場所を変えても構いません。testフォルダの中身、srcフォルダ、readme.txt、makefile以外は基本的に流用すると楽です。
 実行方法ですが、testフォルダに以下のようなファイルhoge.cを作成します。main関数やTest Runnerなどは用意しなくても大丈夫です。

//ファイル名:hoge.c

#include "unity.h"
void setUp(void)
{
}

void tearDown(void)
{
}

void test_hoge(void)
{
    TEST_ASSERT_EQUAL(0, 0);
}

void test_fuga(void)
{
    TEST_ASSERT_EQUAL(5, 5);
}

 そしてexampleフォルダで「rake」コマンドを実行すると、setUp()、tearDown()とともに、test_hoge()、test_fuga()が自動実行され、その結果サマリが表示されます。

組み込みプログラマがステップアップするための推薦書籍

 仕事に慣れ始める時期のせいもあるのか、最近プログラミングで良い参考書籍がないかと聞かれるようになっています。丁度良い機会なので、今回は組み込みプログラマの新人の方向けに、プログラマとしてのステップアップをサポートする推薦書籍を、自分なりにまとめたいと思います。
 想定として、CやC++で一通りプログラミングができるようになった方を対象としています。基礎的・入門的な書籍がほとんどなので、中級者以上の方には平凡かもしれません。なお良書というのは世の中にあふれているので、今回は各トピックごとに2冊づつピックアップするスタイルをとりたいと思います。
 

ソフトウェアの設計をより良くする

 ここで挙げる書籍は、設計のための仕様分析や、モジュールといった上位構造の設計について扱った書籍です。
 なお留意点として、OOPやLL、関数型等の分野で話題となっている現代的・先進的なプログラミングや設計についての解説は、今回挙げる書籍では少ないか古いか適切でありません(良書であるとは思いますが)。このトピックに限りませんが、時代に取り残されないようにするという視点においては、特定の書籍や言語・パラダイムに閉じこもるのはかなりリスクある姿勢です。基本を押さえつつ、補強として常日頃から広い視野で情報収集・勉強を進める姿勢が大事だと思います。

 

組込みソフトウェア開発のための構造化モデリング 要求定義/分析/設計からソースコード作成までソフトウェア開発上流工程の基本を構造化手法に学ぶ

 内容はDFDを主体とした構造化設計の説明が中心です。文章は平易で内容もシンプルです。実践の上では記述不足を感じる所もあるものの、構造化設計の基本的な流れやキーワードは押さえられており、少なくともインデックスとして有効だと思います。また設計品質の章等、重要な内容を優しくシンプルな文章でまとめた優れたテキストも含みます。
 なお設計と言っても、内容は構造設計の前の仕様分析に多くの紙面が割かれています。プログラミングの記述が物足りないなら「組込みソフトウェア開発のためのリバースモデリング (組込みエンジニア教科書)」が重複も多く副読本として読みやすいです。また癖があるので無条件に勧められませんが、DFDでなくUMLを重視したいなら「組込みソフトウェア開発のための オブジェクト指向モデリング (組込みエンジニア教科書)」が副読本として読みやすいと思います。
 

ずっと受けたかったソフトウェア設計の授業

 こちらはJSP/JSDの説明が中心です。DFDや状態遷移の設計もそれなりに言及しています(特にハレルチャートの入門解説として有益だと思います)。前述の本と同じく平易でシンプルな文章ですが、専門用語が多いほか基本概念の説明が中心なため、内容は少し難しくなっています。読む際は、前述の本等で予習しつつ、挙げられているキーワードについて必要に応じて別途ウェブ等で理解を深めておくと無難だと思います。なおこの本は概念的な説明が洗練されていて、中級者以上の人にも考え方や知識を整理できるという点で有効な書籍だと感じます。
 

「リッチなC」から「C++」に進む

 組み込みでもC++はありふれた言語となっていますが、リッチなC言語として手続き型パラダイムのままC++を使用している現場が少なくありません。ここで挙げるのは、そうした状態から脱却するための書籍です。
 

Effective C++ 原著第3版 (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)

 C++プログラマ必読というべき有名な名著です。OOPからメタプログラミングまで、色々なパラダイムでのC++のテクニックを解説しています。なおC++の言語解説の本ではないため、読者はある程度C++を使えるようになっていることが前提です。
 

オブジェクト指向における再利用のためのデザインパターン

 こちらも大変有名な名著です。OOPに則った保守性に優れた設計・実装パターンを解説しています。この本がおすすめなのは実装サンプルがC++である点と、実際の現場を想定したサンプルを多く掲載している点です。最近のデザインパターン本はJavaRubyのようなGCを持つ現代的な言語で書かれたものがほとんどなのでこの本は結構貴重といえます。なお予習・副読本として「デザインパターンとともに学ぶオブジェクト指向のこころ (Software patterns series)」も良書でおすすめです。

 

処理の最適化・効率化を進める

 組み込み開発で日常的に要求される、処理の最適化・効率化について扱った書籍です。今回はアルゴリズム集・データ構造集でなく、それらを求める/使いこなすための考え方について扱った本を挙げています。

 

珠玉のプログラミング—本質を見抜いたアルゴリズムとデータ構造

 処理の効率化について解説が充実しています。この本が良いと思うのは、問題に対して、すぐ答えやアルゴリズムを並べて終わりにするのでなく、問題から答えに行き着くまでの試行錯誤の過程を解説しているところです。それにより処理効率化を進める上での、汎用的な考え方やアプローチを学べるようになっています。

 

Efficient C++パフォーマンスプログラミングテクニック

 こちらはC++の書籍です。処理の実装内容を扱う前述の珠玉のプログラミングと違い、既存のライブラリや言語文法などの使用方法に重きを置いています。効率化と、OOPC++等を活用した良い設計の両立を、プログラミングの工夫によって実現できるという考え方・自信を得る事ができます。

 

プログラミングや開発のスタイルを磨く

 常日頃のプログラミングでより優れたコードを書くための書籍です。
 

プログラミング作法

 自分の学生時代からの愛読書で自分の基礎となっている本です。古い時代の本であるためか、C/C++のサンプルや解説が充実しています。コーディングスタイルからプログラミング、デバッグ、テスト、開発環境と、プログラミングに関わる事柄を時代を超えた洗練された説明で解説しています。

 

Code Complete第2版〈下〉—完全なプログラミングを目指して

 古典の部類に入りつつある本ですが、組み込みC/C++ではまだまだ有益な書籍です。
 上巻では適切な名前や関数の長さといった細かなコーディングの話から、設計やアーキテクチャの話まで、「良いプログラミング」を行うために必要な事柄を徹底的に詳しく書いています。
 一方下巻については、テストや環境について扱っています。ただ下巻は良解説であるとは思うものの、時代遅れになっている説明も結構あるので、ブログや人、最近の書籍等で適宜最新情報を補完しておくのが無難です。
 なおかなりボリュームがあります。読み切れないならば、自分にとって必要だと感じる章をピックアップして断片的に読んでいくのも手です。
 なお最近類書として「Clean Code アジャイルソフトウェア達人の技」が出ています。現代的な開発言語や環境を使っているのなら、コードコンプリートに勝るとも劣らない良書としてお勧めできます。
 

プログラマとしての心構えを学ぶ

 よりよいソフトウェア開発を実現し、またプログラマとして成長していくための情報が記載された書籍です。設計方法やパラダイムが変わっても普遍的に有効な、日頃の勉強方法やキャリアの磨き方について有益な解説があります。その点一番普遍的に価値のある書籍と言えるかもしれません。
 

達人プログラマー—システム開発の職人から名匠への道

 少し古い本ですが、こちらも学生時代から読み続けている自分の愛読書です。ツールや勉強方法、プロジェクト管理など、プログラマとしての身の振り方について解説を行っています。この本は最近でも講演等でよく引用されたり勧められたりしており、時代を超えた名著だと感じます。

 

Clean Coder プロフェッショナルプログラマへの道

 最近出された本です。こちらもプログラマとしての身の振り方について解説を行っています。特に見積もりや業務上の約束事について良い解説があります。職場でトラブルを起こして引きこもりになってしまった、といった、ユーモアの効いた親近感を感じる文章が多いです。

WACATEについて

 現在WACATE2012夏というソフトウェアテストに関する合宿勉強会の準備を進めていて、もうすぐ募集を開始する予定です。


若手エンジニア向けワークショップ:WACATE (ソフトウェアテストワークショップ)


 WACATEは三浦海岸にて一泊二日を使ってソフトウェアテストについて学ぶ合宿勉強会です。2008年より半年に一回開催されてきました。
 ブログでは少ししか触れられていませんでしたが、このWACATEには、自分は参加者として3回、実行委員として3回参加させていただいています。実行委員の立場ではテスト設計やテスト設計技法、バグ管理などをテーマとしたワークショップセッションや講義を担当させて頂きました。丁度良いタイミングなので、今回は紹介がてらWACATEの楽しみ方について簡単にまとめたいと思います。

 なお留意点ですが、以降の文章では興味を持っている人達向けということで、参加者の視点で長所・短所を区別せずあるがままに書いていこうと思います。短所や問題については、今の実行委員の立場では改善対象であり捉え方も変わるものですが、そうした視点は書いている間隅に置いておきます。

前置き:WACATEの特徴

対話やコミュニケーションの学習効果

 以前日科技連のQuality Oneで書かせて頂いた記事(http://www.juse-sqip.jp/vol12/qualityone_02.html)と大分被りますが、WACATEでは対話やコミュニケーションが学習効果や楽しさを支えています。
 例えば課題や講義の内容は(例外も多いですが)基本的に入門者でも参加できる平易なレベルになっています。またそもそも講義の場もそんなに多くありません。例えば講義はほどほどにして、時間のほとんどをワークショップ作業に費やすセッションがしばしば行われています。課題に必要な知識や手法は、そこでは参加者が班単位で経験や知識を出し合って作りだしていくことになります。
 ただそうした一方で、セッション内外では参加者・実行委員・講師間で様々な知見や情報が飛び交っています。例えば応用知識や、技現場で実践する際の工夫、反論・疑問、その他情報源などといったものです。そうした情報共有や議論、対話は、ワークショップの学習効果を補強しており、結果的にWACATEで得られるものを大きく担保していると実感しています。
 実行委員側も対話やグループワーク、コミュニケーションにより生み出されるその補強効果を意識していて、二日間にそれらを促進する仕組みをいろいろと用意しています。例えばグループワークが原則のワークショップ、参加者・講師・実行委員入り乱れて語り合うことを目的としたセッション、宴会、温泉、複数人の相部屋による宿泊等などです。

さまざまな立場で学ぶ

 またこちらも先ほどのQuality Oneで被りますが、WACATEは実行委員・参加者区別なく、勉強したい・活動したいという主体性を持つ人達の集まりのような雰囲気を持っています。
 例えば参加者はベテランから若手まで様々ですが、それぞれ年配者の方はメンタリングを、熟練者の方は議論や情報交換を、若手の方は実践的な勉強を、と様々な楽しみ方をしています。実行委員にしても一種のボランティアみたいなもので、運営や様々な折衝、セッション講師などの経験を積んだり、業界に貢献したりすることを目的に、無償で準備や運営を進めています。言ってしまえば、WACATEはいろいろな人たちの学習意欲や活動意欲を納める器のようなものと感じています。

WACATEとの付き合い方について

 そうした特徴があるので、素でも楽しめはしますが、以下に留意しておくとより楽しめるんじゃないかと個人的に感じています。なおもちろん満たさなくても大丈夫です。こういう楽しみ方もあるよというレベルです。

複数回参加してみる

 複数回参加したり、あるいは実行委員として見回ったりしていると、WACATEでの過ごし方は色々あることを実感させられます。
 例えばWACATEでは5、6人で構成されるグループで二日間を過ごします。そのグループにしても、笑いの絶えない班、静かに効率的に作業する班、激しい議論を行う班など色々な特徴があります。またグループの構成によっても立ち位置が変化します。例えばメンバーが初心者中心であれば技術的なリーダーシップをとる、スペシャリストがいればその人から知見を引き出す聞き手になる といった感じです。
 なのでWACATEを知りたい・楽しみたいという方は、複数回参加してみるといいかもしれません。前述の通りWACATEでは対話や議論、グループワークによる学習効果が侮れないため、異なるメンバー・異なるグループで学習を進めることで、違った雰囲気を楽しめますし、学びや気づきも多種多様であることを実感できます。
 また複数回参加していると知り合いも増えてくるので、議論や情報共有もより活発にできるというメリットもあります。

主体的に勉強する。積極的に勉強機会を確保する。

 前述の通り、WACATEのメインはワークショップであり、扱う講義や課題は入門者でも扱えるような設定がされています。受け身だけでも学べる講義や指導の割合はそんなに大きくありません。
 それに対して、交流や主体的な勉強・実践で学べることが多いほか、高い実力や実績も持つ参加者や講師と議論したり会話したりする機会もあります。深堀すればするほど、学べるものが深まる感じです。
 なので勉強したい内容を見つけて、議論・実践を積極的に行ってみても良いと感じます。またワークショップにしても、単に課題を解くだけでなくそこで学べるものを最大化するように過ごすと、それに応じてより深い知識や経験に触れられるようになります(参加者時代は、課題の忙しさからそれができていなかったのを毎回のように反省してました)。
 逆に主体的に勉強したい・活動したいという心構えがないと、WACATEの効果が目減りしてしまうかもしれません。一応、初心者・初参加者を引き上げる仕組みはありますし、素でも学べるものは多いです。が、参加費2万ちょっとで、一泊二日、また少し都心から離れている(最近は三浦海岸で開催しています)等の制約からWACATEは参加がそれなりに大変なので、結構勿体ないと個人的に感じます。

技術的に尖って議論をふっかける

 議論や情報共有での学習効果が最大になるのは、個性豊かな技術力を持つ人たちがそれぞれ教えあい・学びあうスタイルだと思っています。WACATEも同じで、自分の知識や経験を出しつつ、他の人たちから知識や情報を得ていくことで、用意された講義や課題を越えるものを得られるようになると感じています(特にWACATEはそうしたものを促進する仕組みを用意しているのでなおさらです)。なのでなにかしら強みや意見をもっておいて、ワークショップでぶつけるとりより楽しめるかもしれません。
 なお強みや意見を持つ方法として有効なのが、セッションごとの予習課題です。予習課題は一般的に書籍で、1セッションあたり数冊、2日間合わせて多い時は10冊程度の書籍が予習として提示されます。結構大変ですが、こなすことで議論の基礎が身に付きます。例えばWACATEでは分科会という自由にグループを作ってディスカッションするセッションがありますが、そこでも支障なくいろいろな議題に踏み込んでいけると思います。

さいごに

 WACATEのワークショップの内容は、「JaSST'12 TokyoのTDDセッションとWACATEセッションに登壇 - 千里霧中」でふれているようなものです。あと次回は5、6月ごろから募集開始予定です。詳細は公式サイト(http://wacate.jp/)で掲載する予定ですので、テストに興味ある方は是非参加頂ければと思います。

TDDカンファレンスに参加

 先日TDDカンファレンスに参加。グループディスカッションの合間に急遽ライトニングトークの資料作成・発表をやらせて頂きました。本来LTトーカーとして話す予定でしたが、仕事の遅れで大遅刻してしまい、グループ内のみでの発表となってしまいました・・。主催者の @yujiorama さん大変申し訳ありませんでした。

 なお本当は別の話をしたかったのですが、とても5分で収まらなくなったのと、トラブルで資料作成時間がほとんどとれなくなっていたため、見送る形になってしまいました(上記発表資料は単純に過去の講演資料を急場で継ぎ接ぎしててなんとか仕立てたものです)。内容は固まっていますし、前々から一度言いたかったことなので、それは清書して後ほど公開したいと思います。

xUnit Test PatternsのTest Doubleパターン(Mock、Stub、Fake、Dummy等の定義)

 最近、昔作ったTest Doubleの解説資料を参照・引用してくれる方がちらほら出ていて恐縮しているのですが、見直してみると結構わかりにくい資料なので今回文章としてまとめたいと思います。内容は世間一般的に言われているMock、Stub、Fake、Dummyといった言葉の定義になります。

Test Doubleとは

 Test Doubleとは、テスト実行時に、テスト対象が依存しているコンポーネントと置き換わるものです。「テスト代役」と訳されることもあります。世の中でMock、Stub、Fake、Dummyなどと呼ばれているものの総称に位置づけられます。
 簡単な例を以下に示します。このコードでは、テストメソッド「テストコード()」がメソッド「テスト対象()」をテストしています。また「テスト対象()」は、中でメソッド「外部メソッド()」を実行しています。なお「外部メソッド」はテスト対象でないとします。

@Test
public void テストコード()
{
	AssertEquals(期待値,  テスト対象());
}


int テスト対象()
{
	...
	外部メソッド();
	...
}


 上記の「外部メソッド()」のようなテスト対象の依存要素は、例えば以下の理由でテスト中に実行したくないことがあります。

  • 実行上の制約を持つ。例えば「テスト環境にないハードウェアを制御する」「勝手に変更してはいけない顧客のDBを操作する」「実行にコストがかかる」「OSや外部ライブラリのAPIであり挙動をコントロールできない」といった制約を持つ。
  • テストのために自由に操作したい。例えば『「外部メソッド()」から例外を投げて正しく動くかチェックする』『「外部メソッド()」が呼び出されたかチェックする』のように操作や検証に活用したい

 もし「外部メソッド()」が上記の問題を持っている場合、それをテスト実行時に都合の良い代替と置き換えないといけません。Test Doubleとは、その「外部メソッド()」と置き換わるものが該当します。
 図にすると以下のような感じです。本番環境での依存コンポーネントを、テストの際はTest Doubleに置き換えていきます。


f:id:goyoki:20120301221308p:image


※今回は入門用のテキストということで、コードをかなり簡略化して書いていきます。実際はTest Doubleは一般的にオブジェクトであり、Dependency InjectionやDependency Lookupといった仕組みで本物と置き換えます。詳細は「Test Double Patterns at XUnitPatterns.com 」を参照ください。

xUnit Test PatternsのTest Doubleパターン

 このTest Doubleの定義や分類例には、有力なものにユニットテストの実装パターン集であるxUnit Test Patterns(index at XUnitPatterns.com および同名の書籍)があります。そこではTest Doubleを用途に応じて以下のように分類しています。

  • Test Double Pattern
    • Test Stub
    • Test Spy
    • Mock Object
    • Fake Object
    • (Dummy Object)

 具体的には、Test Doubleに含まれる「Test Stub」「Test Spy」「Mock Oject」「Fake Object」の4つ+Test Doubleに類似した「Dummy Object」の1つの計5つに分類しています。
 なおTest Doubleの定義はいろいろな流儀があり、このxUnit Test Patternsの定義がデファクトスタンダードというわけではありません。ただ分類が明快なほか、Martin Fowlerや id:t_wada さんなどユニットテストの世界で有力な技術者が理解を示しているので、個人的に推奨できる分類と判断しています。それぞれの詳細は後述していきます。

前提:間接入力と間接出力

 なお上記の5つは、おもに間接入力と間接出力の扱い応じて分類されています。具体的な説明に入る前に、その間接入力と間接出力について説明します。

間接入力

 まず間接入力はテストコードから見えないテスト対象への入力です。
 コードでの例を示します。ここではメソッド「テスト対象()」が中で「外部メソッド()」を呼び、その戻り値を使用しています。

int テスト対象()
{
	...
	answer = 外部メソッド();
	...
}

@Test
public void テストコード()
{
	AssertEquals(期待値,  テスト対象());
}

 ここでの「外部メソッド()」の戻り値のように、テストコードから直接見えないがテスト対象に影響を与える入力が、間接入力となります。なお間接入力には、例えばテスト対象が依存するオブジェクトからの例外発生も含まれます。

間接出力

 一方間接出力はテストコードから見えないテスト対象の出力です。
 コードの例を示します。テスト対象が中で「外部メソッド()」を呼んでいますが、そこで「外部メソッド()」に引数を出力しています。

int テスト対象()
{
	...
	外部メソッド(x);
	...
}


@Test
public void テストコード()
{
	AssertEquals(期待値,  テスト対象());
}

 ここでの「外部メソッド()」への引数xのように、テストコードから直接見えないが、テスト対象が外に出力しているものが、間接出力となります。なお間接出力には「外部メソッド()が実行されたか」「複数のメソッドが順番通り呼ばれたか」のようなメソッド呼出しの有無も含まれます。
 間接出力・間接入力の関係図は以下のようになります。


f:id:goyoki:20120301221309p:image

Test Double詳細

 では間接入力・間接出力の定義を踏まえて、各Test Doubleの詳細を説明します。

Dummy Object

 Dummy Objectはテストに影響を与えない代替オブジェクトです。例えば、テスト対象が引数をもっており、テストに影響は与えないがビルドするために何かしらそれを埋めなければならないときに使用されるものです。時にTest Doubleのように依存コンポーネントの代替として使用することもあります。
 かなり極端な例ですが、コードの例を以下に示します(変なコードですがとりあえずTDDでの仮実装中とでも考えてください)。

int テスト対象(Foo foo)
{
	return 10;
}

@Test
public void テストコード()
{
	Foo foo = new Foo();//Dummy Object
	AssertEquals(期待値,  テスト対象(foo));
}

 上記の例では、「テスト対象()」は引数を持ちますが、出力である戻り値「return 10;」と引数は全く無関係です。そこに指定される「foo」がまさにDummy Objectとなります。

Test Stub

 テスト対象への間接入力を操作するTest Doubleは、Test Stubと分類されます。テストでは、任意の間接入力がテスト対象に出力されるように間接入力をTest Stubに事前設定して使用します。例えば以下のような感じです。

//Test Stubとして動作する代替関数
int 外部メソッド()
{
	return 間接入力値;
}

int テスト対象()
{
	...
	answer = 外部メソッド();
	...
}

@Test
public void テストコード()
{
	間接入力値 = 100; //外部メソッド()がテストにとって望ましい値をテスト対象()に返すようセット
	AssertEquals(期待値,  テスト対象());
}

 上記のコードでは、「外部メソッド()」による間接入力「間接入力値」を、テストコード上で事前設定してからテストを実行しています。そのように望ましいように間接入力を操作できるオブジェクトが、Test Stubです。


f:id:goyoki:20120301221310p:image

Test Spy

 テスト対象の間接出力を記録し、それをテストコードから参照可能にするTest Doubleは、Test Spyと分類されます。テストでは、テスト対象の間接出力を記録させ、その後テストコード上でその間接出力を検証します。なお間接入力を操作することもあります。
 例えば以下のような感じです。

//Test Spyとして動作する代替関数
void 外部メソッド(int input)
{
	間接出力値 = input;
}

int テスト対象()
{
	...
	外部メソッド(x);
	...
}

@Test
public void テストコード()
{
	テスト対象();

	AssertEquals(期待値, 間接出力値);//Test Spyに記録させておいた間接出力値を比較検証する
}

 上記のコードでは、「外部メソッド()」への間接出力をいったん「間接出力値」に保持します。テストコードはテスト実行後それを参照することで、間接出力が適切だったかチェックします。こうした間接出力を保持するものがTest Spyとなります。


f:id:goyoki:20120301221311p:image

Mock Object

 以下の用途をあわせもつTest Doubleは、Mock Objectと分類されます。

  • テスト対象の間接出力の期待結果を持っています。
  • テスト対象を実行している間、テスト対象の間接出力を取得します。
  • 間接出力を確保できたら、Mock Objectの中でその期待結果と比較検証し、成功か失敗か判定します。
  • テストコードは、テスト対象を実行後、Mock Objectから検証の成功・失敗の情報を受け取ります。
  • Test Stubの機能を包含していることもあります。

 非常に簡略化した例ですが、例えば以下のようなものです。

//Mock Objectとして動作する代替関数
void 外部メソッド(int input)
{
	if (期待する間接出力値 == input) {
		テスト成功フラグ = true;
	}
	テスト成功フラグ = false;
}

int テスト対象()
{
	...
	外部メソッド(x);
	...
}

@Test
public void テストコード()
{
	期待する間接出力値 = 999;    //Mock内で比較に用いる期待値
	テスト対象();
    AssertTrue(テスト成功フラグ);    //Mock内で検証した結果をチェック
}

 ここでは、「外部メソッド()」が、テスト対象から間接出力を受け取るとともに、同時にその検証も行っています。そしてテストコードでは、その検証結果を見てテスト結果を決めています。このように間接出力の検証能力を持つのがMock Objectです。
 なおMock ObjectとTest Spyは両方とも間接出力を検証するためのTest Doubleです。ただ「Mock ObjectはMock Object内で間接出力結果を評価する」のに対し、「Test Spyは間接出力を保持するだけで、間接出力結果の評価は後からテストコード上で行う」という違いがあります。


f:id:goyoki:20120301221312p:image:w640

Fake Object

 テスト実行中、代替元の本物と同じように動けるTest DoubleはFake Objectと分類されます。Fake Objectは間接出力を受け取り、間接入力を操作しますが、あくまでそれも本物と同じように処理したり出力したりします。

補足

 説明は以上ですが、おまけの補足解説をいくつか行いたいと思います。

分類方法

 Test Doubleの分類方法は以下のような感じです。

  • テストの範囲内で本物と同じように動作するTest DoubleはFake Object。
  • 内部のパラメータや状態がなんでもあってもテストに影響を及ぼさない代替オブジェクトなら、Dummy Object
  • 上記以外で、テスト対象の間接出力を受け取り、かつ自身でその検証を行うTest DoubleはMock Object
  • 上記以外で、テスト対象の間接出力を受け取りそれをあとから参照可能にするTest DoubleはTest Spy
  • 上記以外で、テスト対象の間接入力を操作できるTest DoubleはTest Stub
Test Doubleの用途

 Test Doubleはしばしば以下の用途で用いられます。

  • コスト的・時間的・環境的にテストで実行困難なコンポーネントを置き換える
  • テストを高速化する。例えばDBをメモリ上のFake Objectに置き換えたりする。
  • まだ実装していないコードの代替となる。
  • 複雑で使いにくいコードを簡略化する。例えば多数のクラスや設定に依存しているコンポーネントを、単純なTest Doubleに置き換えてしまう。
  • テスト対象の間接出力を取得し検証する。
  • テスト対象の間接入力を操作する。