概要
アジャイル開発とテスト駆動開発の関係について理解できたので、ここからは実践です。
今回は、CUnitを導入し実行していくところまで行こうと思います。
記事の全体像
- C言語によるアジャイル開発とテスト駆動開発(CI入門) ~ 1.概念 ~
- C言語によるアジャイル開発とテスト駆動開発(CI入門) ~ 2.CUnit導入 ~
- C言語によるアジャイル開発とテスト駆動開発(CI入門) ~ 3.CMake導入 ~
- C言語によるアジャイル開発とテスト駆動開発(CI入門) ~ 4.Jenkins導入 ~
環境
OS : Linux ubuntu 18.04.5 LTS
テストツール : CUnit 2.1-3
コンパイラ : gcc 7.5.0
xUnitとは
xUnit とは、ソフトウェアの単体テスト(Unit Test)を行うためのフレームワークの総称です。
これらを使うことで、ソフトウェアの様々な要素を簡単にテストすることが可能になります。
本記事で扱うCUnit(C)以外にも、JUnit(Java), PyUnit(Python), PHPUnit(PHP)等様々なフレームワークが存在します。
xUnitを導入するメリット
- テストが自動化できる
- 同じテストを何度も書かなくて良い
- ここのテスト結果がどうあるべきかを覚えなくて良い
CUnit導入
では早速CUnitを導入して行きましょう。
最新版が2.1-3ですので、2.1-3を下記コマンドでインストールします。
環境が異なる方は適宜インストールして展開してください。
wget http://jaist.dl.sourceforge.net/project/cunit/CUnit/2.1-3/CUnit-2.1-3.tar.bz2
フォルダを解凍します。
tar xvf ./CUnit-2.1-3.tar.bz2
cd CUnit-2.1-3/
解凍したフォルダの中のREADME
を参考にしながら進めます。
Linux:
In addition to jam, the standard GNU build system is still supported.
The usual sequence of steps should succeed in building and installing CUnit:
1. aclocal (if necessary)
2. autoconf (if necessary)
3. automake (if necessary)
4. chmod u+x configure (if necessary)
5. ./configure --prefix <Your choice of directory for installation>
6. make
7. make install
What's installed:
1. libcunit.a (Library file)
2. CUnit Header files
3. DTD and XSL files supporting xml output files in share directory
4. Man Pages in relevant man directories under the installation path.
5. HTML users guide in the doc subdirectory of the installation path.
6. Example & test programs in the share subdirectory of the install path.
分かりづらかったため、エラー発生時のみ実行結果も載せています。
フォルダ内に aclocal.m4
があるので、 aclocalコマンドは不要かもしれないです。
ワーニングなので、とりあえず進めます。
$ aclocal
aclocal: warning: autoconf input should be named 'configure.ac', not 'configure.in'
autoconfでエラーが出ました。
エラー内容ぐぐると、libtoolをインストールしろとのこと。
$ autoconf
configure.in:161: error: possibly undefined macro: AC_PROG_LIBTOOL
If this token and others are legitimate, please use m4_pattern_allow.
See the Autoconf documentation.
$ sudo apt install libtool -y
autoconfでエラー出た場合は、autoreconf
が必要らしい。。。
$ autoreconf -i
もう一度autoconfから
$ autoconf
$ automake
$ ./configure
$ make
$ sudo install
環境変数LD_LIBRARY_PATHを設定する。
永続化させる場合は、同様の記述を.bashrc
に記述してください。
export LD_LIBRARY_PATH=/usr/local/lib
.bashrc
の場合は反映を忘れずに
source ~/.bashrc
テストの記述
いよいよテスト駆動開発を始めます。
CUnit User Guideを適宜確認しながら記述していきます。
CUnitの一般的な流れは下記の様な流れになります。
- テスト関数の記述(suite, init/cleanup が必要な場合も記述)
- テスト構造を初期化 (CU_initialize_registry())
- テストスイートの追加(CU_add_suite())
- テスト関数の追加 (CU_add_test())
- 適切なインターフェースを使用してテストを実行。(例)CU_console_run_tests)
- テスト構造体のクリーン (CU_cleanup_registry())
テスト関数を下記のように記述しました。
#include <CUnit/CUnit.h>
#include <CUnit/Console.h>
// バグのあるadd関数(テスト対象)
int add(int x, int y) {
return 0;
}
/**
* addテストスイート
**/
// テスト関数001
void test_add_001(void) {
CU_ASSERT_EQUAL(add(1, 2), 3);
}
// テスト関数002
void test_add_002(void) {
CU_ASSERT_EQUAL(add(-1, -2), -3);
}
// main関数
int main() {
CU_pSuite add_suite;
CU_initialize_registry(); // 2.テスト構造の初期化
add_suite = CU_add_suite("add", NULL, NULL); // 3.テストスイートの追加
CU_add_test(add_suite, "test_001", test_add_001); // 4.テスト関数の追加
CU_add_test(add_suite, "test_002", test_add_002); // 4.テスト関数の追加
CU_console_run_tests(); // 5.適切なインターフェースを使用してテストを実行
CU_cleanup_registry(); // 6.テスト構造のクリーン
}
ビルドと実行
さあ、ビルドしましょう
$ gcc unitTest.c -Wall -L/usr/local/lib -lcunit -o unitTest
$ ./unitTest
CUnit - A Unit testing framework for C - Version 2.1-3
http://cunit.sourceforge.net/
***************** CUNIT CONSOLE - MAIN MENU ******************************
(R)un (S)elect (L)ist (A)ctivate (F)ailures (O)ptions (H)elp (Q)uit
Enter command: r
Running Suite : add
Running Test : test_001
Running Test : test_002
Run Summary: Type Total Ran Passed Failed Inactive
suites 1 1 n/a 0 0
tests 2 2 0 2 0
asserts 2 2 0 2 n/a
Elapsed time = 0.000 seconds
***************** CUNIT CONSOLE - MAIN MENU ******************************
(R)un (S)elect (L)ist (A)ctivate (F)ailures (O)ptions (H)elp (Q)uit
Enter command:
上手く行った。やったぜ〜。
おまけ
テスト対象を別ファイルに分割する
上手くCUnitを導入できました。
でも、現状はテストコードとテスト対象コードが同じファイルに存在しています。
これでは実用的ではないですね。
なので、分割します。
unitTest.c
内で定義していたadd()
をadd.c
に抽出し、
add()
の定義をadd.h
に持ってきました。
#include "add.h"
// バグのあるadd関数(テスト対象)
int add(int x, int y) {
return 0;
}
// #ifndef ADD_H_
// #define ADD_H_
int add(int x, int y);
// #endif /* ADD_ */
#include <CUnit/CUnit.h>
#include <CUnit/Console.h>
#include "add.h"
/**
* addテストスイート
**/
// テスト関数001
void test_add_001(void) {
CU_ASSERT_EQUAL(add(1, 2), 3);
}
// テスト関数002
void test_add_002(void) {
CU_ASSERT_EQUAL(add(-1, -2), -3);
}
// main関数
int main() {
CU_pSuite add_suite;
CU_initialize_registry(); // 2.テスト構造の初期化
add_suite = CU_add_suite("add", NULL, NULL); // 3.テストスイートの追加
CU_add_test(add_suite, "test_001", test_add_001); // 4.テスト関数の追加
CU_add_test(add_suite, "test_002", test_add_002); // 4.テスト関数の追加
CU_console_run_tests(); // 5.適切なインターフェースを使用してテストを実行
CU_cleanup_registry(); // 6.テスト構造のクリーン
}
もう一度コンパイルします。
gcc add.c unitTest.c -Wall -L/usr/local/lib -lcunit -o unitTest
実行
$ ./unitTest
CUnit - A Unit testing framework for C - Version 2.1-3
http://cunit.sourceforge.net/
***************** CUNIT CONSOLE - MAIN MENU ******************************
(R)un (S)elect (L)ist (A)ctivate (F)ailures (O)ptions (H)elp (Q)uit
Enter command: r
Running Suite : add
Running Test : test_001
Running Test : test_002
Run Summary: Type Total Ran Passed Failed Inactive
suites 1 1 n/a 0 0
tests 2 2 0 2 0
asserts 2 2 0 2 n/a
Elapsed time = 0.000 seconds
テストを自動実行にする
いい感じになってきました。
あとは、テストを対話型ではなく、自動実行にしたいです。
そのためには、unitTest.cの中身を少し弄ります。
#include <CUnit/CUnit.h>
// #include <CUnit/Console.h> ★ココ
#include <CUnit/Automated.h> ★ココ
#include "add.h"
/**
* addテストスイート
**/
// テスト関数001
void test_add_001(void) {
CU_ASSERT_EQUAL(add(1, 2), 3);
}
// テスト関数002
void test_add_002(void) {
CU_ASSERT_EQUAL(add(-1, -2), -3);
}
// main関数
int main() {
CU_pSuite add_suite;
CU_initialize_registry(); // 2.テスト構造の初期化
add_suite = CU_add_suite("add", NULL, NULL); // 3.テストスイートの追加
CU_add_test(add_suite, "test_001", test_add_001); // 4.テスト関数の追加
CU_add_test(add_suite, "test_002", test_add_002); // 4.テスト関数の追加
CU_automated_run_tests(); // ★ココ
// CU_console_run_tests(); // 5.適切なインターフェースを使用してテストを実行 ★ココ
CU_cleanup_registry(); // 6.テスト構造のクリーン
}
さっそく実行しましょう
gcc add.c unitTest.c -Wall -L/usr/local/lib -lcunit -o unitTest
./unitTest
実行すると、CUnitAutomated-Results.xml
という名前のxmlファイルが吐き出されます。
これを後工程のJenkinsで利用予定です。
CUnitドキュメント
今回の記事を書く上で使用した、CUnitのドキュメントへのリンクの一覧を貼って起きます。
- CUnitのホームページ (CUIからでもダウンロード可)
- CUnitのドキュメント こちらのページにCUnitの使用について大体のことが書いてあります。
- CUnitのドキュメント doxygenにて生成されたドキュメントです
- README ダウンロードしたファイルを解凍したフォルダの中に格納されています。インストール関連について書かれています。
次回
今回までで、何故テスト駆動で開発を進めていくのかについて説明し、xUnitによるテストを導入してきました。
これでいつでもテストを走らせる環境が整いそうです。
でも、まだ手動の箇所が残ってますね。
そうです。ビルド工程です。
次回はこの部分をCMakeによって自動化していきます。
参考文献
CUnit User Guide
CUnitでCプログラムの単体テストをする
CUnitについての備忘録
gcc コンパイルオプション備忘録