Boost.Test (boost/test/included/unit_test.hpp) の使い方

リンク無しでテスト用のソースファイルが複数ある状況でビルドしようとしたら嵌ったお話。

先日、ユニットテストについての講義をする際に「取りあえず Boost.Test を使ってみましょう」と言うことで、リンク(≒ Boost をビルド)しなくても良い boost/test/included/unit_test.hpp を使おうとしたのですが、以下のようなリンクエラーが出力されました(Visual C++ 2012)。

error LNK2005: "public: __thiscall 
boost::unit_test::ut_detail::auto_test_unit_registrar::auto_test_unit_registrar(class 
boost::unit_test::test_unit_generator const &)" (??
0auto_test_unit_registrar@ut_detail@unit_test@boost@@QAE@ABVtest_unit_generator@23@@Z)
は既に gp2-test.obj で定義されています。

…(後略)…

ビルドを通すのにやや苦労しましたが、結論としては「リンク無しで Boost.Test フレームワークを複数のソースファイルが存在する状況で使用する場合」は以下の点を注意する必要があるようです。

  1. #include <boost/test/included/unit_test.hpp> を記述するのは 1 箇所のみ(2 箇所以上でインクルードすると、前述したリンクエラーが発生)。main.cpp 的なファイルを作成して、そこでインクルードするのが良いか。
  2. それ以外のファイル(各テストを記述したファイル)については、リンクしない場合であっても
    #include <boost/test/unit_test.hpp> と記述する。
  3. このままだと Boost.Test 用のライブラリをリンクしようとしてエラーになるので、コンパイル時に BOOST_TEST_NO_LIB または BOOST_ALL_NO_LIB を定義してリンクしないようにする。

ソースファイルの構成例

1. main.cpp

#define BOOST_TEST_MAIN // or #define BOOST_TEST_MODULE test_module_name
#include <boost/test/included/unit_test.hpp>

2. test1.cpp

#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE(test_case1) {
    BOOST_CHECK(true);
}

3. test2.cpp(以下、必要なファイル数だけ同様に)

#include <boost/test/unit_test.hpp>

BOOST_AUTO_TEST_CASE(test_case2) {
    BOOST_CHECK(true);
}

コンパイル時に BOOST_TEST_NO_LIB 指定を忘れずに。気を付けないと嵌りますが、リンク有無に関わらずメインファイル以外のインクルード記述は変わらないので、どちらにも移行しやすいと言うのは利点かもしれません。