Boost logo

Boostテストライブラリ: ユニットテストフレームワーク

受入テストによって、顧客は満足できる。
ソフトウェアが提供するビジネス価値が、顧客がお金を払いたいと
思うようなレベルを満足することが分かるからである。
ユニットテストによってプログラマは満足する。
プログラマが思う通りにソフトウェアが動作することを保障するからである。

XPの格言

ホーム
イントロダクション
テスト入門
フレームワークコンポーネント

テストケース
テストスイート
テスト結果
テストログ
テストモニター

フレームワークの統合
フレームワークのパラメータ
フレームワークのコンパイル
拡張
サンプル、テストプログラム
理論
設計

関連事項: テストツール

イントロダクション

Boostテストライブラリのユニットテストフレームワークは テストツール を使ったテストケースを作成し、それらを テストスイート階層内に整理するのを簡便化するツールを提供する。 このフレームワークは、ユーザを面倒なエラー検出・報告作業や変数処理から解放してくれる。 また、フレームワークを初期化したり、コマンドライン引数や環境変数からパラメータを設定したり、 ユーザ提供のinit_unit_test_suite(argc, argv)関数を読んだり、提供されたテストスイートを実行 するmain()関数も提供される。 このフレームワークは、テストツールの表明(assertion)の成功/失敗を すべて記録し、すべてのテストケース中の実行中のテストケース数というような進捗を表示し、何種類かの フォーマットの結果レポートを生成することができる。 ユニットテストフレームワークはシンプルなテストから複雑で重要なテストまで使用することができる。 プログラム実行モニターを使用するような、製品コード内ではこの フレームワークは使用することができない。 このフレームワークはライブラリのオフラインで実装するように設計されている。インライン実装によって、 コンパイルの実行効率が向上する。ユニットテストフレームワークはあたらしいテストプログラムを作成する 間はプログラム実行モニターよりも好きになるべきである。 reference to the top

テスト入門

ユニットテストフレームワーク事始

フレームワークコンポーネント

ユニットテストフレームワークはいくつかの協調して動作するコンポーネントから構成されている。 すべてのコンポーネントはboost::unit_test_framework名前空間に配置されている。 テストケースコンポーネントはシンプルなテストのユニット(単体)という 考えをカプセル化している。 テストスイートコンポーネントは関連するテストユニットをひとまとめにし、 テストのユニットの合成として扱うようにすることができる。 テスト実行中の出力の生成を管理するのには、テストログコンポーネントを使用する ことができる。 テスト結果コンポーネントはテスト結果の表現の責任を持つ。

テストケース

ユニットテストフレームワークを使用することで、単体の関数やユーザの作成したクラスメソッドなどによる テストケースを作成することができる。以下の4つのテストケースがある:function_test_case, class_test_case, parametrized_function_test_case, parametrized_class_test_case。 これらすべてのテストケースインタフェースの実装は、抽象ベースクラスtest_caseを元に定義されている。

テストケースインタフェース

定義

unit_test_suite.hpp内で定義されている。

概要
class test_case
{
public:
    void set_timeout( int timeout );
    void set_expected_failures( unit_test_counter exp_fail );

    void run();
};
説明

抽象クラスtest_caseはテストケースのインタフェースを定義している。 test_case::set_timeout(...)を使用して、テストケースの タイムアウト時間を設定することができる。 execution_monitor::execute(...)メソッドを見ると、タイムアウト値に関する 詳しい情報がある。test_case::set_expected_failures(...)メソッドを使用すると、 テストケース内での期待するテストツールの失敗数を記述することができる。 ほとんどの場合、このtest_caseをtest_suiteに追加する歳にこれらのパラメータを設定するのが便利であろう。 test_suite::add(...)には詳細情報が書いてある。test_case::run() メソッドを使用すると、テストケースの実行を開始することができる。

構築

test_caseクラスのインスタンスを生成する必要はない。

reference to the top

単体の関数ベースのテストケース

定義

unit_test_suite.hpp内で定義されている。

概要
class function_test_case : public test_case
{
public:
    function_test_case( void (*function_type)(), char const* name );

    ... // 実装
};
説明

もっともシンプルで幅広く使用されるテストケースである。クラスfunction_test_caseのインスタンスは ユーザが指定した、void (*fct)()という型の単独の関数へのポインタを使用して生成される。返り値の型はvoidである。 テスト結果を通知するには、返り値ではなく、テストツールを用いて行う。

構築

テスト関数ベースのテストケースを作成するには、以下のマクロを使用する:

BOOST_TEST_CASE( &free_function )

BOOST_TEST_CASEはfunction_test_caseクラスの新しいインスタンスを作成し、 基底クラスである、test_caseクラスへのポインタを返す。 このポインタはtest_suite::add(...)

メソッドの引数に使用されることが多い。
サンプル
void test_feature1()
{
    ...
}
...

ts->add( BOOST_TEST_CASE( &test_feature1 ) );
_____________________
#include <boost/test/unit_test.hpp>
using boost::unit_test_framework::test_suite;

// テストケースを単体の関数として実装する
void free_test_function()
{
    BOOST_CHECK( 2 == 1 );
}

test_suite*
init_unit_test_suite( int argc, char* argv[] ) {
    test_suite* test= BOOST_TEST_SUITE( "Example" );

    test->add( BOOST_TEST_CASE( &free_test_function )

    return test;
}
reference to the top

クラスメンバー関数ベースのテストケース

定義

unit_test_suite.hpp内で定義される。

概要
template<class UserTestClass>
class class_test_case : public test_case
{
public:
    class_test_case( void (UserTestCase::*function_type)(),
                     char const* name,
                     boost::shared_ptr<UserTestCase>& user_test_case );

    ... // 実装
};
説明

class_test_caseのインスタンスは、メンバー関数と、void (UserTestClass::*fct)()という型を持つ、テストクラスの インスタンスを用いて生成される。同じインスタンスを共有することを許し、一つのテストケースはもう一つの結果を 使用することができるようにするために、class_test_caseはboost::shared_ptrをユーザのテストケースクラスのインスタンス を用いて生成される。同じ効果を以下のような単独の関数を用いても達成することができる:

void compount_test() {
    UserTestCase instance;

    instance.test1();
    instance.test2();
    ...
};
この方式の1つの欠点はテストケースの結果を分離することができないということである。 クラスメンバー関数ベースのテストケースを用いるもう一つの理由は、デフォルトコンストラクタ を持たないテストケースをテストする場合である。言い換えれば、ユーザテストケースがコマンドライン 引数や、他のパラメータが生成時に必要となる場合である。一般的にはclass_test_caseはテストロジックを 単独の関数に実装することができない場合にのみ使用することを勧める。class_test_caseを実装する例としては、 function_test_caseをより長期間使用するかどうか、ということである。
構築

class_test_caseのクラスのインスタンスを生成するには以下のマクロを使用する:

BOOST_CLASS_TEST_CASE( function, shared_test_case_instance ).

BOOST_CLASS_TEST_CASEは新しい、class_test_caseクラスのインスタンスを作成し、 基底クラスである、test_caseクラスへのポインタを返す。 このポインタはtest_suite::add(...)

メソッドの引数に使用されることが多い。
サンプル
class my_complex_test {
public:
    void test_feature1() {
        ...
    }
};
...

ts->add( BOOST_TEST_CASE( &my_complex_test::test_feature1 ) );
_______________________________
class class_under_test {
public:
    // iは正でなければならない; それ以外では例外を投げる
    explicit class_under_test( int i );
    ...
    // アクセスメソッド
    int get_value() const;
};

class compound_test {
public:
    void test_construction() {
        BOOST_CHECK_THROW( new class_under_test( -1 ) );

        v = new class_under_test( 1 );

        BOOST_CHECK( v is valid );
        ...
    }

    void test_access_methods() {
        BOOST_CHECK_EQUAL( v->get_value(), 1 );
        ...
    }
private:
    class_under_test* v;
};
...

boost::shared_ptr<compound_test> instance( new compound_test );
ts->add( BOOST_TEST_CASE( &compound_test::constructor, instance ) );
ts->add( BOOST_TEST_CASE( &compound_test::test_access_methods, instance ) );
reference to the top

パラメータを持つ、独立関数のテストケース

定義

unit_test_suite.hpp内で定義される。

概要
template <typename ParamIterator, typename ParameterType>
class parametrized_function_test_case : public test_case
{
    ... // 実装
};
説明

parametrized_function_test_caseクラスのインスタンスはユーザの提供する、 void (*fct)( ParameterType )という型を持つ独立した関数を与えて生成される。

構築

parametrized_function_test_caseのインスタンスは以下のマクロで生成する:

BOOST_PARAM_TEST_CASE( free_function, first_parameter, last_parameter ).

first_parameterとlast_parameterはパラメータのリストの最初のイテレータと最後のイテレータである。 BOOST_PARAM_TEST_CASEはparametrized_function_test_caseクラスの新しいインスタンスを作成し、 基底クラスである、test_caseクラスへのポインタを返す。 このポインタはtest_suite::add(...)

メソッドの引数に使用されることが多い。 parametrized_function_test_caseはパラメータのリストを内部に保存することはしない。そのため、テストケースが 実行されるまではパラメータリストは破壊してはならない。そのため、パラメータリストはinit_unit_test_suite のローカル変数に作成しな方が良い。 パラメータリストの寿命を制御するシンプルな方法としては、ユーザ定義のテストスイートクラス内に定義するというのがある。
サンプル
void test_file_reader( std::string const& file_name )
{
    ...
}

struct reader_testing : public boost::unit_test_framework::test_suite
{
    void reader_testing()
    {
        files_to_test.push_back( "file 1" );
        ...

        files_to_test.push_back( "file N" );

        add( BOOST_TEST_CASE( &test_file_reader,
                              files_to_test.begin(),
                              files_to_test.end() );
    }
    std::list<std::string> files_to_test;
};
...
ts->add( new reader_testing );
reference to the top

パラメータを持つクラスメンバー関数ベースのテストケース

定義

unit_test_suite.hppで定義される。

概要
template<class UserTestClass, typename ParamIterator, typename ParameterType>

class parametrized_class_test_case : public test_case
{
    ... // 実装
};
説明

parametrized_class_test_caseのインスタンスは、void (UserTestClass::*fct)( ParameterType )という型を持つ、 ユーザのテストクラスのメソッドを渡して生成される。parametrized_class_test_caseはユーザのテストクラスの インスタンスの生成と破壊の責任を持つ。

構築

parameterized_class_test_caseクラスのインスタンスは以下のマクロで生成する:

BOOST_PARAM_TEST_CASE( test_class_method, first_parameter, last_parameter ).

first_parameterとlast_parameterはパラメータのリストの最初のイテレータと最後のイテレータである。 BOOST_PARAM_TEST_CASEはparametrized_class_test_caseクラスの新しいインスタンスを作成し、 基底クラスである、test_caseクラスへのポインタを返す。 このポインタはtest_suite::add(...)

メソッドの引数に使用されることが多い。 parametrized_class_test_caseはパラメータのリストを内部に保存することはしない。そのため、テストケースが 実行されるまではパラメータリストは破壊してはならない。そのため、パラメータリストはinit_unit_test_suite のローカル変数に作成しな方が良い。 例えば、パラメータリストをファイルスコープ内に置くことができる。
サンプル
class my_complex_test
{
    void test_assignment( double tolerance )
    {
        ...
    }
};
...
std::list<double> possible_tolerances;
ts->add( BOOST_TEST_CASE( &my_complex_test::test_assignment,
                          possible_tolerances.begin(),
                          possible_tolerances.end() ) ); 
reference to the top

テストスイート

定義

defined in unit_test_suite.hpp

概要
class test_suite : public test_case
{
public:
    void add( test_case* tc,
              unit_test_counter expected_failures = 0,
              int timeout = 0 );
    ... // Implementation
};
説明

ユニットテストフレームワークでは作成したテストケースをテストスイートでまとめ、任意の深さにテストを階層化することができる。 test_caseをtest_suiteに追加するにはtest_suite::add(...)メソッドを用いる。最初のパラメータは新しいtest_caseのポインタ であり、ふたつめは期待される失敗の数である。これはテストツールの失敗する表明の数である。 みっつめはテストケースのタイムアウト時間である。期待される失敗の数はテストスイート内で自動的に計算されるため、 アテストスイートを階層に追加する時にいちいち指定する必要はない。 execution_monitor::execute(...)にタイムアウトの詳細な説明がある。 残りのふたつのオプションはオプションであり、値を入れない場合には設定されない。この場合、test_case内で定義された値が使用される。 reference to the top

構築

test_suiteクラスのインスタンスを作成するためには以下のマクロを使用する:

BOOST_TEST_SUITE( test_suite_name ).

BOOST_TEST_SUITEはtest_suiteクラスのインスタンスを作成し、そのポインタを返す。 test_suiteはtest_suiteであり、複数レベルの階層を作成することができる。

テスト結果

定義

defined in unit_test_result.hpp

概要
class unit_test_result
{
      static unit_test_result& instance();
      void confirmation_report( std::ostream& where_to );
      void short_report( std::ostream& where_to );
      void detailed_report( std::ostream& where_to );
      int result_code();
};
説明

ユニットテストフレームワークはテスト結果をunit_test_resultクラスのインスタンスとして保持する。 unit_test_resultクラスは3つのメソッドを結果報告のために持っている。 unit_test_result::confirmation_report(...)は成功/失敗のメッセージのみを報告する。 unit_test_result::short_report(...)は現在のテストケースの結果を報告する。 このレポートには成功や失敗したテストの数、失敗したテストツール表明などが含まれる。 unit_test_result::detailed_report(...)メソッドはこれらが含まれる、すべてのテストの結果を返す。 ほとんどの場合ではこれらのインタフェースを直接利用することはないだろう。 テスト結果の報告はフレームワークによって自動的に行われる。

構築

unit_test_resultクラスのインスタンスにアクセスするには、 静的なunit_test_result::instance()メソッドを使用する。 reference to the top

テストログ

定義

unit_test_log.hpp内で定義される。

概要
class unit_test_log
{
     static unit_test_log& instance();
     void  set_log_stream( std::ostream& str );
     void  set_log_threshold_level_by_name( char const* lev );
};
説明

テスト出力ストリームを管理するために、ユニットテストフレームワークではunit_test_logというシングルトンクラスを使用している。 テスト出力ストリームを変更するためにはunit_test_log::set_log_stream(...)メソッドを使用する。 デフォルトのストリームはstd::coutである。 ログレベルを変更するにはunit_test_log::set_log_level_by_name(...)メソッドを使用する。 ほとんどの場合はこのインタフェースを直接利用することはない。 ログレベルの変更はテストプログラムの外部から簡単に行えるようになっている。 このパラメータと設定可能な値はフレームワークのパラメータを参照。

構築

unit_test_logクラスのインスタンスにアクセスするには、 静的なunit_test_log::instance()メソッドを使用する。 reference to the top

テストモニター

テストケースの実行を監視するために、ユニットテストフレームワークはunit_test_monitorクラスを使用する。 unit_test_monitorはtest_caseのメソッドを、実行モニター下で実行させる。 この環境では、execution_monitor例外を指定されたエラーコードに変換する。 unit_test_monitorクラスの詳細はBoostテストライブラリ設計に書いてある。 ほとんどの場合ではテストケースが実行される場合にはすでに監視が行われるため、unit_test_monitorを使用するコードを書く必要はない。

フレームワークの統合

説明

ユニットテストフレームワークはテスト環境を整備し、結果の報告まで行うmain関数が用意されているという説明を行った。 ユーザが提供する関数へのフックもこのmain関数には含まれている。 以下の形式を持つ関数を定義することでユーザ提供の関数とフレームワークは統合される:

boost::unit_test_framework::test_suite* init_unit_test_suite ( int argc, char* argv[] )

この関数はtest_suiteクラスのルートのインスタンスを初期化するのに使用するべきである。 NULLポインタをこの関数で返すと、初期化されていないtest_suiteとして扱われる。 この場合はフレームワークは提供されているtest_suiteクラスのインスタンスで実行を行い、 プログラムの最後で破壊される。そのため、そのインスタンスは動的に割り当てられていなければならない。 コマンドライン引数はフレームワークで使用されるオプションというように保障されるわけではない。

テストケースが実行されると、フレームワークは結果を報告し、リターンコードを返す。 テストプログラムから返された返り値のリストはユニットテストフレームワークで統合される:

意味
boost::exit_success もしテスト中にエラーがなかったり、フレームワークパラメータの返り値結果コードで定義されている成功結果コードになった場合に返される。
boost::exit_test_failure 深刻なエラーが検知されなかったり、キャッチされなかった例外がなかった場合に返される。
boost::exit_exception_failure 深刻なエラーが検知されたり、キャッチされなかった例外があった場合に返される。
サンプル
test_suite*
init_unit_test_suite( int argc, char* argv[] )
{
    test_suite* test= BOOST_TEST_SUITE( "Master test suite" );

    test->add( BOOST_TEST_CASE( &my_test_function ) );

    return test;
}
reference to the top

フレームワークのパラメータ

ユニットテストフレームワークは外部からテストプログラムを設定する方法を二つ用意している。 コマンドライン引数と、環境変数である。以下のテーブルはフレームワークのパラメータを列挙したものである。 環境変数、コマンドライン引数のどちらも設定しなかった時のデフォルト値は、太字であらわしてある。

パラメータ名: Log level
環境変数名: BOOST_TEST_LOG_LEVEL
コマンドライン引数名: --log_level=<value>
設定可能な値:
all - 通ったテストも含めてすべてのログメッセージを表示する
success - allと同じ
test_suite - テストスイートのメッセージを表示する
messages - ユーザメッセージを表示する
warnings - ユーザによる警告を表示する
all_errors - すべてのエラーを表示する
cpp_exception - キャッチされなかったC++例外を表示する
system_errors - システムが報告する、致命的ではないエラーを表示する (タイムアウトや浮動小数点例外など)
fatal_errors - システムやユーザに由来する、致命的なエラーを表示する (不正メモリアクセスなど)
progress - 進捗状況のみを報告する: すべてのテスト数と現在実行したテストの対など
nothing - 何も表示しない
説明: ログレベルを設定する。成功したときにテストスイートメッセージを含めて表示されるのから、 成功しても出力ストリームに何も表示されない、ということもできる。

パラメータ名: [Not] return result code
環境変数名: BOOST_TEST_RESULT_CODE
コマンドライン引数名: --result_code=<value>
設定可能な値: yes
no
説明: Noが指定された場合には結果コードとして常にゼロを返さなければならない。 GUIに統合されたテストプログラムで使用される場合が考えられる。

パラメータ名: Result report level
環境変数名: BOOST_TEST_REPORT_LEVEL
コマンドライン引数名: --report_level=<value>
設定可能な値: no
confirm
short
detailed
説明: フレームワークがテストの最後に生成する、結果レポートのレベルを設定する。 "no"を指定すると、結果レポートが省略される。

パラメータ名: Save pattern
環境変数名: BOOST_TEST_SAVE_PATTERN
コマンドライン引数名: --save_pattern=yes
設定可能な値: no
yes
説明: output_test_streamツールのセーブモードを使用するかどうかを設定する。

パラメータ名: Print build info
環境変数名: BOOST_TEST_BUILD_INFO
コマンドライン引数名: --build_info=yes
設定可能な値: no
yes
説明: プラットフォーム、コンパイラ、STL、使用しているBoostのバージョンなどを含む、フレームワークのビルド情報を表示する。

reference to the top

フレームワークのコンパイル

ユニットテストフレームワークはオフラインのライブラリ、あるいはテストプログラムに対してリンクするという、ふたつの方法がサポートされている。 Boostテストライブラリのソースディレクトリにある、以下のファイルがフレームワークに含まれる:

execution_monitor.cpp
test_tools.cpp
unit_test_parameters.cpp
unit_test_log.cpp
unit_test_main.cpp
unit_test_monitor.cpp
unit_test_result.cpp
unit_test_suite.cpp

また、フレームワークを構成するすべてのファイルをテストモジュールに直接インクルードする方法もある。 <boost/test/included/unit_test_framework.hpp>のファイルを、この目的で使用することができる。

サンプルとテストプログラムreference to the top

unit_test_example1
unit_test_example2
unit_test_example3
unit_test_example4
unit_test_example5
online_test
errors_handling_test
parameterized_test_test
result_report_test

理論

新しいライブラリ、クラス、プログラムなどに対して作業を行うときにはまず何を行うだろうか? 我々はまさにユニットテストモジュールから始める。一方、フレームワークはつまらない作業を大量にさせることもある。 我々は小さな大量のテストケースを書けるようにしたいのである。そしてそれらをスイートにまとめたいのである。 最初はすべてのエラーを見つけるために、後退テストの間はできるだけ多くの説明的なエラーメッセージを読むべきである。 小さなテストプログラムであれば、実行時間よりもコンパイル時間の方が時間がかかる。 誰が1秒のテストのために1分も待つだろうか?長く複雑なテストであればテストの進捗が見える方が良い。

設計

reference to the top

Boostテストライブラリ設計のドキュメントではBoostテストライブラリ間のコンポーネントの関係について説明している。