背景
Pythonでの単体テストのやり方が書いてあるテスト駆動Pythonを読んで、実際の業務で実践していました。
その効果を話していたところ、組み込み開発チームでも導入していきたいということだったので、組み込み開発でテスト駆動開発を行うテスト駆動開発による組み込みプログラミングを参考にしながら、Cpputestを導入しました。
その際の導入手順をここにまとめます。
目的
- 実機がなくても開発したコードの動作が検証可能な環境を構築する
- ホスト(PC)での単体テスト実行環境を構築
- ターゲット(組み込みLinux基板)での単体テスト実行は未実施
環境
- OS: Debian GNU/Linux 9 on WSL
- テストフレームワーク: Cpputest 3.8
インストール手順
-
Cpputestをダウンロード
- 後にCPPUTEST_HOMEという環境変数にCpputestをビルドしたディレクトリにパスを通すので、自分で管理しやすいパスにCpputestをgit cloneします。今回はhomeディレクトリにダウンロードします
$ cd ~ $ git clone https://github.com/cpputest/cpputest.git
-
Cpputestをビルド
$ cd cpputest $ autoreconf . -i $ ./configure $ make tdd
-
環境変数CPPUTEST_HOMEを定義する
$ export CPPUTEST_HOME=~/cpputest
-
Cpputestが提供するユーティリティスクリプトをインストール
$ export PATH=$PATH:${CPPUTEST_HOME}/scripts $ chmod a+x ${CPPUTEST_HOME}/scripts/NewProject.sh
実例
実際に新規プロジェクトを作成し、単体テストを作成する例を以下に示します。
簡単のために、足し算を行うadd関数の本体とそのテストを具体例として取り扱います。
-
Cpputestが提供するプロジェクト作成スクリプト(NewProject.sh)を使ってプロジェクトを作成する
$ cd ~ $ NewProject.sh add Copy template project include Project.cproject ProjectMakefile Project.project src tests Change Name ./ProjectMakefile to addMakefile Change Name ./Project.project to add.project Change Name src/util/ProjectBuildTime.cpp to addBuildTime.cpp Change Name include/util/ProjectBuildTime.h to addBuildTime.h Change Name tests/util/ProjectBuildTimeTest.cpp to addBuildTimeTest.cpp You might want to modify the path for CPPUTEST_HOME in the Makefile.
実行後、フォルダ構成は以下のようになっています。
add ├── include │ └── util │ └── addBuildTime.h ├── Makefile ├── src │ └── util │ └── addBuildTime.cpp └── tests ├── AllTests.cpp └── util └── addBuildTimeTest.cpp
-
テストが実行されるかを確認する
$ cd add $ make compiling AllTests.cpp compiling addBuildTimeTest.cpp compiling addBuildTime.cpp Building archive lib/libadd.a a - objs/src/util/addBuildTime.o Linking add_tests Running add_tests . OK (1 tests, 1 ran, 1 checks, 0 ignored, 0 filtered out, 0 ms)
-
add関数の単体テストtest_add.cppを作成する
$ touch tests/test_add.cpp
test_add.cpp#include "CppUTest/TestHarness.h" extern "C" { #include "add.h" } TEST_GROUP(add) { void setup(){}; void teardown(){}; }; TEST(add, InputPlusValue) { int ret = add(3, 4); CHECK_EQUAL(ret, 7); }
-
作成した単体テストが実行されているか確認する
$ make compiling test_add.cpp tests/test_add.cpp:5:17: fatal error: add.h: No such file or directory #include "add.h" ^ compilation terminated. /mnt/j/workspace/tdd_emb/cpputest/build/MakefileWorker.mk:506: recipe for target 'objs/tests/test_add.o' failed make: *** [objs/tests/test_add.o] Error 1
当たり前といえば当たり前ですが、add.h, add.cに関して何も実装していないのでコンパイルエラーがでます。逆に言うと、コンパイルエラーが出ているのでCpputestはTEST(add, InputPlusValue)を認識していると言えます。
-
単体テストが実行され失敗することを確認する
まずは、コンパイルが通るところまでを確認します。
includeディレクトリとsrcディレクトリにそれぞれadd.hとadd.cを作成し、add関数のインターフェイスと何を入力されても0を返す空実装を行います。$ touch include/add.h src/add.c
add.h#ifndef _ADD_H_ #define _ADD_H_ int add(int, int); #endif /* _ADD_H_ */
add.cint add(int a, int b) { return 0; }
また、自動生成されたMakefileを変更します。
INCLUDE_DIRSにある include/*をinclude/utilに変更します。
そして、テストをビルドするとテストが失敗します。$ make compiling AllTests.cpp compiling test_add.cpp compiling addBuildTimeTest.cpp compiling add.c compiling addBuildTime.cpp Building archive lib/libadd.a a - objs/src/add.o a - objs/src/util/addBuildTime.o Linking add_tests Running add_tests . tests/test_add.cpp:18: error: Failure in TEST(add, InputPlusValue) expected <0> but was <7> difference starts at position 0 at: < 7 > ^ . Errors (1 failures, 2 tests, 2 ran, 2 checks, 0 ignored, 0 filtered out, 3 ms)
これで、最初に作成した単体テストが実行され期待した値と違う結果が返されたことが確認されました。この状態からadd関数の本実装を行います。
-
テストが通るようにadd関数を実装する
ここは自分で修正してください。以下のような結果が得られるはずです。$ make compiling add.c Building archive lib/libadd.a r - objs/src/add.o r - objs/src/util/addBuildTime.o Linking add_tests Running add_tests .. OK (2 tests, 2 ran, 2 checks, 0 ignored, 0 filtered out, 1 ms)
まとめ
Cpputestの導入方法とそれを使った簡単なサンプルをまとめました。
テスト駆動開発による組み込みプログラミングには、もっと複雑な例とテスト駆動を組み込み環境で行うにはどうしたらよいかの豊富な実例が示されておりますので、気になった方は是非お読みください。そして、教えてください。