CppUnitの使い方

とっかかりの手助けになるようように、少しだけ使い方を書いておきます。

単純なテスト・ケース

自分で書いたコードが本当に動くものかどうか知りたいとき、どうしますか? いろいろなやり方があります。デバッガを使ってステップ実行をする、ストリームにコードの出力を吐き出すなどは、いちばん簡単なやり方でしょう。しかし、どちらにも欠点があります。ステップ実行はいい考えですが、自動的に行うことはできません。変更するたびに毎回自分でやらなければなりません。ストリームに吐き出すのも悪くはありませんが、コードは汚くなるし、いつもは必要でない情報まで垂れ流されてしまいます。

CppUnitを使ったテストは、自動的に実行できます。設定は簡単だし、一度テストを書いてしまえばずーっと使い続けて、自分のコードの品質を信頼できるものにすることができます。

単純なテストをするためには以下のようにします:

TestCaseクラスのサブクラスを作ります。メソッド"runTest()"をオーバーライドします。値をチェックしたいときには、"assert (bool)"を呼びます。引き数はテストが成功のときにtureを返すような式です。

例えば、複素数クラスの等値比較子をテストしたいときには以下のように書きます:

	class ComplexNumberTest : public TestCase { 
	public: 
                    ComplexNumberTest (string name) : TestCase (name) {}
        void        runTest () {
                        assert (Complex (10, 1) == Complex (10, 1));
                        assert (!(Complex (1, 1) == Complex (2, 2)));
                    }
        };

この例はとても単純な場合でした。普通は小さなテストをいっぱい書いて、一回の実行で全部テストしたいでしょう。そのためにはフィクスチャ(fixture)を使います。

 

フィクスチャ

フィクスチャとは、テスト・ケースのもととなるオブジェクトの集合です。開発しながらテストを書くのにとても役に立ちます。このやり方を試しながら、フィクスチャの使い方を学んでいきましょう。ちょうど複素数クラスを開発しているところだとしましょう。まずは、Complexという名前の空のクラスを定義するところから始めます。

	class Complex {}; 

さて、上記のComplexNumberTestのインスタンスを生成します。コードをコンパイルし、何が起きるか見てみましょう。最初にいくつかのコンパイラ・エラーがあることが分かります。テストでは演算子==を使っていますが、まだ定義されていないからです。これを直しましょう。

	bool operator== (const Complex& a, const Complex& b) { return true; }

では、テストを再度コンパイルして実行してみます。今度はコンパイルは通りましたね。でもテストは失敗したはずです。演算子==が正しく動くようにするためには、もう少し仕事が必要です。コードに手を加えましょう。

	class Complex { 
        friend bool operator== (const Complex& a, const Complex& b);
        double      real, imaginary;
        public:
                    Complex ()  {
                    real = imaginary = 0.0;
                    }
        };

        bool operator== (const Complex& a, const Complex& b)
        { return eq(a.real,b.real) && eq(a.imaginary,b.imaginary); }

今度はコンパイルして走らせると、テストをパスするはずです。

そろそろ新しい演算子と新しいテストを追加してもよさそうです。このときにフィクスチャが役に立ちます。テストをするときに複素数のインスタンスを数個作って、テストを通じて再利用することにすれば便利だと思いませんか?

そのためには以下のようにします:

  1. フィクスチャの各々に対して、メンバ変数を追加する
  2. "setUp ()"をオーバーライドして、変数を初期化する
  3. "tearDown ()"をオーバーライドして、"setUp ()"でアロケートした永続的なリソースをすべて解放する
	class ComplexNumberTest : public TestCase  {
	private:
        Complex 	*m_10_1, *m_1_1; *m_11_2;
	protected:
	void		setUp ()  {
			    m_10_1 = new Complex (10, 1);
			    m_1_1  = new Complex (1, 1);
			    m_11_2  = new Complex (11, 2);  
                        }
	void		tearDown ()  {
			    delete m_10_1, delete m_1_1, delete m_11_2;
			}
	};

いったんこのフィクスチャを作ってしまえば、複素数の加算のテスト・ケースを追加するときや、その他にも開発途中で必要な場合にはいつでも使い回しが効きます。

 

テスト・ケース

ではフィクスチャを使って、それぞれのテストを書いて動かすにはどうすればいいでしょう?

この過程には次の二つのステップが必要となります:

  1. フィクスチャ・クラスのメソッドとしてテスト・ケースを書く
  2. そのメソッドを走らせるTestCallerを生成する

前のテスト・クラスに、テスト・ケースのメソッドをいくつか追加してみました:

	class ComplexNumberTest : public TestCase  {
	private:
        Complex 	*m_10_1, *m_1_1; *m_11_2;
	protected:
	void		setUp ()  {
			    m_10_1 = new Complex (10, 1);
			    m_1_1  = new Complex (1, 1);
			    m_11_2 = new Complex (11, 2);  
                        }
	void		tearDown ()  {
			    delete m_10_1, delete m_1_1, delete m_11_2;
			}
	void		testEquality ()  {
			    assert (*m_10_1 == *m_10_1);
			    assert (!(*m_10_1 == *m_11_2));
			}
	void		testAddition ()  {
			    assert (*m_10_1 + *m_1_1 == *m_11_2);
                 	}
	};

各々のテスト・ケースに対して、インスタンスを生成して実行します:

	test = new TestCaller<ComplexNumberTest>("testEquality", ComplexNumberTest::testEquality);
        test->run (); 

TestCallerのコンストラクタの2番目の引き数は、ComplexNumberTestのメソッドのアドレスです。TestCallerを実行すると、ここで指定したメソッドが走ります。

このようにしてテストが溜まってきたら、一つのスィート(Suite)にまとめます。

 

スィート

テストを全部ひとまとめにして走らせることができるようにするにはどうすればいいでしょう?

CppUnitにはTestSuiteというクラスがあって、複数個のTestCaseをまとめて実行することができます。例えば一つのテスト・ケースを走らせるには、以下のようにします:

	TestResult result;
	TestCaller<ComplexNumberTest> test ("testAddition", ComplexNumberTest::testAddition);
	Test.run (&result);

 

二つ以上のテストのスィートを作るには、次のようにします。

	TestSuite suite;
	TestResult result;
	suite.addTest (new TestCaller<ComplexNumberTest>("testEquality", ComplexNumberTest::testEquality));
	suite.addTest (new TestCaller<ComplexNumberTest>("testAddition", ComplexNumberTest::testAddition));
	suite.run (&result);
           

TestSuiteはTestCaseを呼び出すだけではありません。Testインタフェースを実装したオブジェクトならば、どんなものでも呼び出すことができます。例えば、あなたがコード中でTestSuiteを生成して、私も自分のを生成したとしましょう。両方を一緒に走らせるためには、両方を含んだTestSuiteを作って走らせればいいのです:

	TestSuite suite;
	suite.addTest (ComplexNumberTest.suite ());
	suite.addTest (SurrealNumberTest.suite ());
	suite.run (&result);

 

テスト・ランナー

テストを実行して、その結果を集めるにはどうすればいいでしょう?

テスト・スィートを作ったら動かしたいですよね。CppUnitではそのために、動かすテストを定義して、その結果を表示するツールを用意してあります。TestRunnerプログラムに、テスト・スィートを返す静的メソッドsuiteを用意して、TestRunnerプログラムから動かしたいテスト・スィートにアクセスできるようにします。例えば、ComplexNumberTestスィートをTestRunnerから実行できるようにするには、ComplexNumberTestに以下のコードを追加します:

	public: static Test *suite ()  {
	    TestSuite *suiteOfTests = new TestSuite;
	    suiteOfTests->addTest (new TestCaller<ComplexNumberTest>("testEquality", testEquality));
	    suiteOfTests->addTest (new TestCaller<ComplexNumberTest>("testAddition", testAddition));
            return suiteOfTests;
	}

CppUnitは、TestRunnerのテキスト版とMicrosoft Visaul C++ 5.0(6.0でもいいはず)のグラフィクス版の両方のツールを用意しています。別のプラットホームで動かしたい場合には、グラフィクス版のコードを見てみて下さい。移植は簡単だと思います。

テキスト版を使うには、TestRunner.cppに以下のヘッダ・ファイルをインクルードします:

	#include "ExampleTestCase.h"
	#include "ComplexNumberTest.h"

そして、"main ()"関数に"addTest (string, Test *)"の呼び出しを追加します:

	int main (int ac, char **av)  {
	    TestRunner runner;
	    runner.addTest (ExampleTestCase::suite ());
	    runner.addTest (ComplexNumberTest::suite ());
	    runner.run ();
	    return 0;
	}

こうすればTestRunnerでテストを実行することができます。すべてのテストをパスすれば、その旨メッセージが出るでしょう。もしテストに失敗したら、以下の情報を表示します:

  1. 失敗したテスト・ケースの名前
  2. そのテストを含むソース・ファイルの名前
  3. 失敗の起きた行番号
  4. 失敗を検知したアサート文に含まれるテキスト全体

CppUnitは失敗とエラーを区別します。失敗はアサーションが予期してチェックしたものです。エラーはゼロ除算やその他の例外(C++ランタイムに由来するもの、ユーザ・コードに由来するもの)のように、予期しなかった問題です。

MS Developer's Studioを動かしている場合には、より易しいGUI版を使うことができます。このためにculib, TestRunner, HostAppの三つのプロジェクトがあります。これらはそれぞれ、フレームワーク用の静的ライブラリ、ダイアローグ・ベースのTestRunner(DLL)、これらを動かすアプリケーションの例です。開発中のアプリケーションにTestRunnerを取り込むには、静的ライブラリとTestRunner DLLをリンクします。TestRunner DLLはアプリケーションのホーム・ディレクトリ、システム・ディレクトリ、PATH上のいずれかになければならないことに注意。アプリケーションの中では、テストを走らせたいところでTestRunnerDlgのインスタンスを生成します。ダイアログ・オブジェクトに走らせたいテストを渡して、実行します。

実際のTestRunnerのスクリーン・ショットです:

 

CppUnitの実装に関する情報はこれ以外にREADME.HTMLにも書いてあります。