boost の Unit Test Framework を使ってみた。
日本語の情報が少なくて、色々とはまったのでメモ。
使用したバージョンは 1.40です。たぶん、1.37以降では使えると思います。環境はWindowsなので、Visual Studio 2008を使用しています。
インストール
http://www.boostpro.com/download からインストーラをDownloadしてきます。メールアドレスなどの登録が必要ですが、Freeです。
インストール時に、コンパイルが必要なライブラリのうちインストールするものを聞かれるので、unit test frameworkにチェックを入れておきます。
インストール後、Visual Studioを起動して includeと libディレクトリの設定を行います。ツール→オプション→プロジェクトおよびソリューション→VC++ ディレクトリ の画面で、ディレクトリを表示するプロジェクト→インクルード ファイル を選択し、「C:\Program Files\boost\boost_1_40」を追加*1。同じく、ライブラリ ファイル を選択し、「C:\Program Files\boost\boost_1_40\lib」を追加*2。
テストの準備
テスト用に C++で空のプロジェクトを作成します。
以下の2つのファイルを追加します。
これで、Visual Studio上にてDebugで実行すると「出力ウィンドウ」にテスト結果が表示されます。
basic_dostream.h
// via http://www.codeproject.com/KB/debug/debugout.aspx #pragma once #include <windows.h> #include <ostream> #include <sstream> #include <string> template <class CharT, class TraitsT = std::char_traits<CharT> > class basic_debugbuf : public std::basic_stringbuf<CharT, TraitsT> { public: virtual ~basic_debugbuf() { sync(); } protected: int sync() { output_debug_string(str().c_str()); str(std::basic_string<CharT>()); // Clear the string buffer return 0; } void output_debug_string(const CharT *text) {} }; template<> void basic_debugbuf<char>::output_debug_string(const char *text) { ::OutputDebugStringA(text); } template<> void basic_debugbuf<wchar_t>::output_debug_string(const wchar_t *text) { ::OutputDebugStringW(text); } template<class CharT, class TraitsT = std::char_traits<CharT> > class basic_dostream : public std::basic_ostream<CharT, TraitsT> { public: basic_dostream() : std::basic_ostream<CharT, TraitsT> (new basic_debugbuf<CharT, TraitsT>()) {} ~basic_dostream() { delete rdbuf(); } }; typedef basic_dostream<char> dostream; typedef basic_dostream<wchar_t> wdostream;
test_main.cpp
//#define BOOST_TEST_DYN_LINK // DLLを使用する場合 #define BOOST_TEST_NO_LIB // staticにリンクする場合 #ifdef _DEBUG #include <boost/test/unit_test.hpp> #include <boost/test/unit_test_log.hpp> #include <iostream> #include "basic_dostream.h" dostream trace; boost::unit_test_framework::test_suite* init_unit_test_suite( int argc, char* argv[] ) { // ログの出力先を変更(指定しないとデフォルトではstd::cout) boost::unit_test::unit_test_log_t::instance().set_stream( trace ); std::cerr.rdbuf( trace.rdbuf() ); return BOOST_TEST_SUITE("replace output stream"); } #endif #define BOOST_TEST_MODULE boost_unit_test #include <boost/test/included/unit_test.hpp>
ユニットテストを書く
例えば、以下のクラスをテストすることにします。
foo.h
#pragma once #include <exception> class Foo { public: int add(int a, int b) const { return a + b; } double add(double a, double b) const { return a + b; } void throwA() { throw std::exception(); } };
そのためには、以下のような2通りの書き方ができます。
まずは、単純な場合。
foo_test.cpp
#include <boost/test/unit_test.hpp> #include "foo.h" namespace{ BOOST_AUTO_TEST_SUITE(foo_test_case) // test suiteの名前 BOOST_AUTO_TEST_CASE(test_add) // testの名前 { Foo f; BOOST_CHECK_EQUAL(3, f.add(1, 2)); // CHECKでは失敗しても、次の行へ進む。 BOOST_REQUIRE_EQUAL(4, f.add(1, 3)); // REQUIREの場合には、失敗した場合そこでテストメソッドの実行が終わる。 } BOOST_AUTO_TEST_CASE(test_add2) { Foo f; BOOST_CHECK_CLOSE(3.0, f.add(1.0, 2.0), 1e-6); // doubleをチェックする場合には、CLOSEで比較を行う。 BOOST_REQUIRE_CLOSE(4.0, f.add(1.0, 3.0), 1e-6); } BOOST_AUTO_TEST_CASE(test_throwA) { Foo f; BOOST_CHECK_THROW(f.throwA(), std::exception); // 例外が投げられるテストを行う場合には、THROWを使う。 } BOOST_AUTO_TEST_SUITE_END() }
上記の場合には、いわゆる setUpや tearDownに当たる処理が書けないため、必要な場合には以下のようにする。
foo_test2.cpp
#include <boost/test/unit_test.hpp> #include "foo.h" namespace{ struct Fixture { Foo* pf; Fixture() { pf = new Foo(); // setUP } ~Fixture() { delete pf; // tearDown } }; BOOST_FIXTURE_TEST_SUITE(new_foo_test_case, Fixture) BOOST_AUTO_TEST_CASE(test_add) { BOOST_CHECK_EQUAL(3, pf->add(1, 2)); // Fixtureで初期化した値が使える。 BOOST_REQUIRE_EQUAL(4, pf->add(1, 3)); } BOOST_AUTO_TEST_SUITE_END() }
あとは、テストを増やしたい場合には、foo_test.cppや foo_test2.cppと同様に bar_test.cppなどをプロジェクトに追加すればOKです。