C言語向けユニットテスティングフレームワーク Unityについて

 C言語ユニットテストフレームワークには、昔はCUnit+自前のテストコードジェネレータを使用していたのですが、前々からUnityの使用機会が増えています。
 理由は色々ありますが、自分の場合だと以下の点で便利なのが特に大きいです。

  • 移植性に優れるため組み込み環境(ホスト、ターゲット両方で)でも使える
  • 標準で自動テストディスカバリ機能(テストランナーの自動生成)を持っているので、そのままTDDのような軽快なテスティングを実現できる

 特に組み込みCでTDDをやる場合は、PCUnitと並ぶ有望なテスティングフレームワークだと感じます。
 少し前にTest Driven Development for Embedded Cの読書会にて解説を行う機会もあったので、今回Unityについて簡単にまとめたいと思います。

特徴

  • 組み込み開発も対象に含むC言語向けユニットテスティングフレームワーク
  • サイト: Unity - Compact Test Framework for C
  • シンプルなフレームワーク。標準ANCI C準拠の3つのファイルでコアを構成。rubyスクリプトで機能を拡張する。そのためマルチプラットフォーム。組み込みのプアなターゲット環境でも動作する。
  • rubyを使った現代的な拡張機能を持つ。例えばCppUTestとの互換や、CMock等をサポート。
  • xUnitxUnit Test Patternsに従った構成や命名、実行フロー。CUnitよりこちらの方がxUnitファミリーとしてよりふさわしい。
  • rubyスクリプトを使ったテストランナー自動生成をサポート。テストメソッドを書いたら自動的に見つけ出して実行してくれる。

 

基本構成

 テストメソッドは以下のような感じです。Assertionメソッドは「TEST_ASSERT_EQUAL」などを使います。「TEST()」がテストメソッドです。

#include "unity.h"

TEST(TestSuiteName, TestCaseMethodName)
{
    TEST_ASSERT_EQUAL(3, TestTarget());
}


 Four Phase Testもサポートしています。

#include "unity.h"

TEST_GROUP(TestSuiteName);

//setup
TEST_SETUP(TestSuiteName)
{
...
}
//teardown
TEST_TEAR_DOWN(TestSuiteName)
{
...
}


 なおテストランナーを手動で実装する場合は以下のようなコードが必要になります。面倒ですが、CUnitに比べればシンプルです。

//Test Case Methodを実行
//SetupとTeardownは定義されていれば各Test Case Methodごとに実行されます
TEST_GROUP_RUNNER(TestSuiteName)
{
    RUN_TEST_CASE(TestSuiteName, TestCaseMethodName);
    …(TestSuiteNameに属する他のTestCaseMethodを同じように羅列)…
}
//Test Suiteを実行
static void RunAllTests(void) 
{
    RUN_TEST_GROUP(TestSuiteName);
    …(他のTestSuiteを同じように羅列)…
}

//スタートアップ
int main(int argc, char * argv[]) 
{
    return UnityMain(argc, argv, RunAllTests); 
}

 

Rubyスクリプトによるテストコード生成&実行

 UnityはRubyスクリプトによる実行が大変便利です。とりあえずTest Runnerの自動実行についてまとめます。

 
 まずRubyスクリプトのサポートを得るには、RubyとRakeをインストールして実行可能にしておきます。
 次にスクリプトの実行環境を構成します。スクリプトの実行にはHelperコードやymlファイル、rbファイルなどが必要です。それらに関しては、Unityが使い勝手の良いサンプルを十分に用意しているので、それを流用して環境を構成すると無難に確保できます。(なおサンプルの環境は、クロス開発でのユニットテストに対する、Unity開発者達のアプローチや戦略を学べる良い教材でもあります)
 とりあえずUnityのダウンロードファイルを解凍すると、「docs」「src」「build」などのフォルダがトップに展開されます。そのうち以下のフォルダをテスト環境を構築したいフォルダに丸ごとコピーします。

  • auto
  • example
  • src
  • targets

 このうち、autoに機能拡張のためのRubyスクリプトが、targetsに開発環境ごとの設定をまとめたymlファイルが、srcにUnity本体が置いてあります。またユーザはexampleにテストコードを書いていきます。
 exampleフォルダはテストプロジェクトフォルダでもあるので、適宜名前を変えましょう。exampleフォルダには以下があります。

  • build
  • helper
  • test
  • makefile
  • rakefile_helper.rb
  • rakefile.rb
  • readme.txt

 このうち、testにテストコードを格納します。srcにテスト対象を置いていますが、適宜場所を変えても構いません。testフォルダの中身、srcフォルダ、readme.txt、makefile以外は基本的に流用すると楽です。
 実行方法ですが、testフォルダに以下のようなファイルhoge.cを作成します。main関数やTest Runnerなどは用意しなくても大丈夫です。

//ファイル名:hoge.c

#include "unity.h"
void setUp(void)
{
}

void tearDown(void)
{
}

void test_hoge(void)
{
    TEST_ASSERT_EQUAL(0, 0);
}

void test_fuga(void)
{
    TEST_ASSERT_EQUAL(5, 5);
}

 そしてexampleフォルダで「rake」コマンドを実行すると、setUp()、tearDown()とともに、test_hoge()、test_fuga()が自動実行され、その結果サマリが表示されます。