Boost.勉強会 #8 大阪 に参加してきました。発表者、運営者の皆様お疲れ様でした。今回もいろいろと黒魔術的な発表があり「これが変態かー」と感慨深く見ていたのですが、それはそれとして、個人的に気になっていたテーマとして「C++ でのテスト」に関連する発表が 2 件ほどあったので、楽しみにしていました。
C++ のテストフレームワークも数多く公開されており、好みや用途によって選択肢が変わって来るのですが、まぁ今だと選択肢として挙がるのは「Boost.Test か googletest か」になってくるのかなと思います。どちら(もしくは、上記以外を含めたどれか)を選択すべきか、と言う問いに対しては、残念ながら現状でも決め手となるものに欠けているのですが、自分が利用する上で選択する上でのポイントを少し検討してみます。
手軽に利用できる
発表中でもありましたが、Boost.Test には「ヘッダファイルのインクルードのみで利用可能なテストフレームワーク」と言うものが用意されており、比較的小さなライブラリ等のプログラムをテストする際には便利でよく利用しています(参考:Boost minimal test - Faith and Brave - C++で遊ぼう)。
ビルドしてリンクする位どうって事ないと感じたりもするのですが、特に、Windows (Visual C++) 上で開発している時には /MT、/MD の違いや x86/x64 の違いで予想外にリンクに手こずる事もあり、リンク作業を敬遠しがちになったりもします。そう言った事もあって「ヘッダファイルのインクルードのみで利用可能」と言うのは魅力的に感じる時も多々あり、boost/test/minimal.hpp にはよくお世話になっています。
部分的にテスト可能
C++ Unit Test Frameworks - ACCU を読んでいて思い出したのですが、「部分的にテスト可能かどうか」と言う点も選択肢を検討する上でポイントになったりします。例えば、Microsoft .NET Framework 系?のプログラムのテストフレームワークである NUnit だと下記のように、テストしたい部分のみを選択して実行できるようになっています。
部分的にテストを行える機能があると、ネットワーク通信用のライブラリ等の「時間のかかる」テストも「テーストハーネス内に記述はするが、通常時(何度も実行する時)は実行されないようにしておく」のような選択肢を取る事もできます。
単体テストは速く走る。速く走らないとしたら、それは単体テストではない。他の種類のテストが単体テストの仮面をかぶっていることもよくある。次に当てはまるものは単体テストではない。
- データベースとやり取りをする
- ネットワークを介した通信をする
- ファイルシステムにアクセスする
- 実行するために特別な環境設定を必要とする(環境設定ファイルの編集など)
上記に該当するテストが悪いというわけではない。多くの場合において、そのようなテストを書く価値はあり、しばしばテストハーネス内に記述される。しかし単体テストは、そのようなテストと切り分けて、変更を行うたびに高速で実行できるように保ち続けることが重要である。
レガシーコード改善ガイド(p.17)
部分的にテストを行う機能については、ざっと見たところ Boost.Test、googletest ともに可能なようです。
ディフォルトでは,Google Testはユーザが定義したすべてのテストを実行します.しかし,ときにはテストの一部だけを実行したい場合もあるでしょう(たとえばデバッグや変更の速やかな確認のために).GTEST_FILTER環境変数か,--gtest_filterフラグをセットすることで,このフィルタに一致するテストだけを実行することが可能となります(形式は,TestCaseName.TestNameです)
GoogleTestAdvacnedGuide(翻訳)
これをひとまとめにしてテストされるプログラムも参照可能にしてコンパイルすると、すべてのテストを実行してくれるテストプログラムができる。WARNの表示や一部のテストだけを実行したいときは、コマンドラインで引数を指定すればよい。
(例) --log_level=warning (エラーレベルがWARNのところも表示) --run_test=suite名,suite名/test名 (特定のテストだけ実行)Boost Testメモ
CppUnit と CppUnitLite
最後に蛇足的な話題。C++ のテストフレームワークと言うと CppUnit もよく名前が挙がるのですが、もう少し簡単に記述できるテストフレームワークとして CppUnitLite と言うものがあるそうです。
最初に CppUnit を開発した時、私はできるだけ JUnit に近いものにしようと考えました。そのほうが xUnit のアーキテクチャを見たことがある人にとって使いやすいだろうと考えたからです。しかしすぐさま、C++ ではきれいに実装することが難しい、あるいは不可能な事柄が次々に出てきました。これは C++ と Java の機能の違いによるものです。一番の問題は C++ にリフレクションの機能がないことでした。Java では、派生クラスのメソッドに対する参照を持って、実行時にメソッドを検索するといったことが可能です。しかし C++ では、実行時にアクセスすべきメソッドを登録するコードは人間が書いてやる必要があります。その結果、CppUnit は少し使いにくく理解づらいものになりました。
・・・(中略)・・・
CppUnit も CppUnitLite もテストハーネスとして利用可能です。CppUnitLite を使って書いたテストのほうが少し短いため、本書の C++ の例では CppUnitLite を使用しています。
レガシーコード改善ガイド(p.57-59)