「ソフテックだより」では、ソフトウェア開発に関する情報や開発現場における社員の取り組みなどを定期的にお知らせしています。
さまざまなテーマを取り上げていますので、他のソフテックだよりも、ぜひご覧下さい。
ソフテックだより(発行日順)のページへ
ソフテックだより 技術レポート(技術分野別)のページへ
ソフテックだより 現場の声(シーン別)のページへ
以前、C言語ライブラリの開発を担当した時に、ライブラリの単体テスト(ユニットテストとも呼ばれる)の自動化対応を行いました。今回のソフテックだよりでは、その時の取り組みについて紹介します。
ライブラリは汎用性の高い複数のプログラムを再利用可能な形でひとまとまりにしたもので、他のプログラムから呼び出されて利用されます。
単体テストは、関数やメソッドなどの小さな単位で検証するテストのことですが、この時のライブラリ開発ではライブラリを呼び出すテストプログラムを作成して単体テストを行いました。
開発したライブラリは約800の関数がありましたが、テストプログラムが自動で作成でき、テスト結果を自動で判定ができれば効率アップになり、確認ミスも防げると考え、単体テストの自動化について検討することにしました。
第277号(2017年3月1日発行)技術レポート『WindowsアプリケーションにおけるMSTestの活用』で紹介したMSTestや、第293号(2017年11月1日発行)技術レポート『アプリケーション開発におけるCppUnitの活用』で紹介したCppUnitなど、単体テストを自動化するためのテスト用フレームワーク(ツール)は多く存在します。
最初は存在を知っていたMSTestを使用できないかと考えましたが、開発ライブラリはマルチOS動作(WindowsやLinux)を前提としており、MSTestはWindowsのVisual Studio上で動作するツールであるため、使用しませんでした。検討していくうちに自前で対応するのが1番早く対応できそうであったため、仕組みを検討して対応することにしました。
検討した仕組みは次の(1)〜(7)のフローになります。設計作業などの開発工程の記載は省略しています。
(1)〜(7)をフローチャートにまとめると図 1のようになります。
図1. 単体テストの自動化のフローチャート (クリックで拡大)
繰り返しの説明になる部分もありますが、次に具体的な関数を使用した例で説明します。
割り算を行うDIV関数の単体テストを行う場合を例に説明していきます。
bool DIV(short int dividend, short int divisor, short int *answer)
{
// 入力値のエラーチェック
bool result = CheckDivParam(dividend, divisor);
if (result)
{
*answer = dividend / divisor;
}
else
{
*answer = 0;
}
return result;
}
図2. ライブラリ関数の例
表1. 関数情報の設定
関数名 | 戻り値の型 | 引数の入出力 | 引数名 | 引数の型 |
---|---|---|---|---|
DIV | bool | IN | dividend | short int |
IN | divisor | short int | ||
OUT | answer | short int |
void Test_DIV(void)
{
CTestProject testProjct;
std::string strPath = TESTDATA_PATH;
strPath += "TestData_DIV.csv";
int max = testProjct.SetCsvFile(strPath);
for (int line = 1; line <= max; line++)
{
// 入力値読み込み
short int dividend = testProjct.GetDataToInt16(line, "dividend");
short int divisor = testProjct.GetDataToInt16(line, "divisor");
// 期待値読み込み
bool exp_result = testProjct.GetDataToBool(line, "result");
short int exp_answer = testProjct.GetDataToInt16(line, "answer");
// ライブラリ関数実行
short int answer;
bool result = DIV(dividend, divisor, &answer);
// 結果判定
testProjct.AreEqual(1, line, "DIV", "result", exp_result, result);
testProjct.AreEqual(1, line, "DIV", "answer", exp_answer, answer);
}
}
図3. 自動生成したテストプログラム
表2. テスト項目書例
No. | テスト関数 | 項目 | 入力 | 出力(期待結果) | テスト結果 | |||||
---|---|---|---|---|---|---|---|---|---|---|
dividend | divisor | result | answer | 結果 | 確認日 | 確認者 | Ver | |||
1-1 | DIV | 被除数が正の最大 | 32767 | 12 | true | 2730 | ||||
1-2 | 除数が正の最大 | 34 | 32767 | true | 0 | |||||
1-3 | 被除数が0 | 0 | 56 | true | 0 | |||||
1-4 | 除数が0(0除算エラー) | 78 | 0 | false | 0 |
No.1-1 : DIV result = OK(true)
No.1-1 : DIV answer = OK(2730)
No.1-2 : DIV result = OK(true)
No.1-2 : DIV answer = OK(0)
No.1-3 : DIV result = OK(true)
No.1-3 : DIV answer = OK(0)
No.1-4 : DIV result = OK(false)
No.1-4 : DIV answer = NG(0, 98) ★エラー
図4. テスト結果ログファイルイメージ
この単体テストの仕組みのメリット・デメリットについて説明します。
メリットテストケースやライブラリ関数が増えてもテストプログラムのプログラミングが不要であるため、極端な話、仕様を理解できている人であればテストを実施することが可能です。
開発したライブラリは関数に渡した値が同じであれば、何回関数呼び出しを行っても実行結果は必ず同じになるライブラリ(例えば、割り算の関数で10 ÷ 5を何度実行しても結果は2になる)であったため、問題ありませんでした。
しかし、関数の実行条件やタイミングによって実行結果が変わるライブラリの場合は使用できません。
構造体や配列が引数の関数やC++クラスの単体テストの場合は、生成されたテストプログラムそのままではコンパイルエラーとなるため、手直ししなければなりませんでした。
今後も有効なツールがあれば有効活用し、もし目的に合ったツールがなければ仕組みを検討して導入し、開発効率アップに役立てていきたいと思います。
(M.A.)
関連ページへのリンク
関連するソフテックだより