#[サマリ]C/C++でBDDしたいならiglooとcmakeの組み合わせがオススメです
日本語だとびっくりするくらい情報が無いですが、Stackoverflowだとちょくちょく名前が出てきています
=> igloo - BDD Style Unit Testing for C++
xUnit系だとgoogletestがほぼ鉄板になりつつありそうな雰囲気がしますが、BDD系だと鉄板だといえるのが出ていない感あります。CSpec/CppSpecなどが候補に上がりますが、マクロがDSLとして作られすぎているとか、C言語のライブラリへのテストに向かないような仕様になっているものとかもあり。
そこで探していたところ、iglooがC言語でBDDするのにも対応できそうだし、DSLとしてのシンタックスも簡易で、何よりインストール不要でgit submodule
でブチ込めばすぐに使えるというのが気に入りました。
ついで、今までビルドには素直にmake使ってたのですが、ファイルの依存性管理とテスト用のビルド設定でMakefileがグチャグチャになりがちで、試しにcmakeを手習いがてら使ってみたらかなり捗る感じに開発が進むようになりました。
というわけでオススメします。
この記事は新規プロジェクトからigloo, cmakeを導入してBDDワークフローに至るまでの導入ドキュメントです。
#(vimeo)igloo作者が投稿したライフゲームのBDDワークフロー
作者のJoakim Karlsson氏がvimeoに投稿したスクリーンキャストが分かりやすいです
=> (vimeo) C++ TDD Kata: Conway's Game of Life
#gitと組み合わせてリポジトリのセットアップ
以下の手順はC言語でアプリケーション開発する、という前提です。
##1.ディレクトリ階層
ふつうの構成です
project_root/
|- include/ <- 開発する本体のヘッダファイル置き場 (*.h/*.hpp)
|- extlib/ <- git submodule で管理する外部ライブラリとか
|- src/ <- 実装コード置き場(*.c/*.cpp)
|- t/ <- テストコード置き場 (*.cpp)
|- bin/ <- 実行ファイル置き場、要 gitignore
|- .gitignore
|- CMakeList.txt
|- README
|- LICENSE
\_ etc ...
そういえばC言語だとテスト用ディレクトリはなんかt
一文字で済ますのをよく見かけるけど、これ慣例なのでしょうか
##2.git init
して.gitignore
に幾つかトラッキングしない設定を書く
他の.gitignore
な設定は省略しています。抜粋
# cmake artifacts
MakeFile
cmake_install.cmake
CMakeChache.txt
CMakeFiles/
bin/
cmake
使った事ない人は逆にMakeFileがgitにトラッキングされないのに最初不安に覚えるかもしれません。(自分もそうでした)
が、慣れてくると「あ、もうCMakeListだけでいいや…」って感覚になってきます
##3.extlibにgit submodule
でiglooの安定版をリポジトリに登録させる
$ cd ~/repo/project_root/
$ cd ./extlib/
$ git submodule add https://github.com/joakimkarlsson/igloo.git
$ pushd ./igloo/
$ git checkout igloo.1.1.1
$ cd ../../
$ git add extlib/igloo/
$ git commit -m "[Added] (submodule) Igloo BDD framework"
##4.CMakeList.txtにiglooとテストディレクトリへのパスを作る
とりあえず最初はBDDできる最小限の設定だけ書きます
# お持ちのcmakeのバージョンを指定
cmake_minimum_required(VERSION 2.8)
# executable なターゲットの投げ先を指定できます
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ./bin)
# 普通にプロジェクト名入れてね
project(wonderfull_very_very_nice_project)
# 最初からバージョン番号入れておいた方がいいです
set(serial "0.1.0")
# インクルードするヘッダがある場所をcmakeに教えてあげる
include_directories("${PROJECT_SOURCE_DIR}" extlib/igloo include)
# テストを実行する上で依存関係のあるファイル群をこうやって指定できます
file(GLOB TestSourceFiles t/*.cpp src/*.c)
# 実行ターゲットの追加
add_executable(run_test ${TestSourceFiles})
# ターゲットに対し、ビルド後に任意のコマンドを実行させられます
# 以下の例はテストのソースをコンパイル後にテスト実行まで自動化するようにしたもの
add_custom_command(
TARGET run_test
POST_BUILD
COMMAND run_test
WORKING_DIRECTORY ./bin)
##5.テストランナーのmain.cpp
を置く
テストコードのディレクトリのトップにmain.cpp
を置きます。
#include <igloo/igloo.h>
using namespace igloo;
int
main(int argc, const char* argv[])
{
return TestRunner::RunAllTests(argc, argv);
}
これで開発体制が整いました。
この時点でgit commit
してmaster
ブランチからgit flowなりgithub-flowなりをする起点にするとよいかと思います
##6.あとは普通のBDDサイクルを繰り返す
###6-a.まずテスト書く
=> テストの書き方は本家を参考にしてください
一応軽く、C言語で書いたライブラリのテストの場合だとこう書くってサンプル書いておきます。
なお、Context - Spec
な、ストーリーベースなBDDの場合は <igloo/igloo.h>
を、
Describe - It / When - Then
な、オブジェクトベースなBDDの場合は <igloo/igloo_alt.h>
を
インクルードします。
#include <igloo/igloo_alt.h>
using namespace igloo;
/* ここまでテンプレ */
#include "my_linked_list.h" /* テスト対象のAPIが宣言されてるヘッダ */
Describe(MyLinkedList)
{
linked_list_t *list;
void SetUp()
{
list = new_linked_list();
}
void TearDown()
{
free_linked_list(list);
}
It(Has_no_member_when_initialized)
{
Assert::That(is_list_empty(list), IsTrue());
}
}; // Describe(){}; はセミコロン必須
###6-b.cmake CMakeList.txt
してビルド設定を更新
- 新規にテストコードが書かれたファイルを追加した時
- 新規にヘッダファイルを追加した時
- 新規に実装コードが書かれたファイルを追加した時
- ビルド設定を変更した時
以上のどれかの場合は以下のコマンドでMakefileを更新します
$ cmake CMakeList.txt
これで、cmakeが新しいファイルをビルド設定に追加してくれます。便利ですね
いちいち CMakeList.txt
を指定する必要もなく、
$ cmake .
これで十分です
###6-c.make
でテスト走らせる
$ make
Scanning dependencies of target run_test
[ 50%] Building CXX object CMakeFiles/run_test.dir/t/linked_list.cpp.o
.../.../.../t/linked_list.cpp:XX:XX error: ...
当然最初はコンパイルエラーで終わります。
最初はそもそも
- ”hoge.hってincludeしてるけどそんなファイルねぇよ!バカ!”
- "hogehoge(foo);って呼んでるけどそんなAPIねーじゃねーか!バカ!"
- "ヘッダには宣言されてるけど実装どこにもねーじゃねーか!バカ!"
- "…!…!…もうバカ!”
っていっぱい怒られますけど、負けないでください
###6-d.実装して 6-b -> 6-c の繰り返し
普通にヘッダ書いて、実装して
$ cmake .
$ make clean
$ make
の繰り返しです。ファイルを追加してない、ビルド設定を変えてない、などであれば make
コマンドだけで良いです。
好みのスクリプト言語でエディタにフックかけておくと捗ると感じる人もいるかもしれません。
(自分はよくtypoしたり、細かい変数のリネームが多いのでエディタにフックはしない派ですが)