SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

C/C++に対応した、もうひとつのUnitTestFramework ─ WinUnit

お手軽お気楽なWindows C/C++ テスト環境

提供されるASSERT群

 ヘッダ「WinUnit.h」の中に「WIN_」で始まる一連の検証マクロ群が用意されており、テスト関数内でこれらを使うことでテスト結果の検証を行います。

  • WIN_ASSERT_EQUAL(expected, actual, ...)
  • expected(期待値)とactual(結果)とが等しい(==)ことを検証します。
  • WIN_ASSERT_NOT_EQUAL(notExpected, actual, ...)
  • expected(期待値)とactual(結果)とが等しくない(!=)ことを検証します。
  • WIN_ASSERT_STRING_EQUAL(expected, actual, ...)
  • expected/actualchar*もしくはwchar_t*。文字列として等しいことを検証します。
  • WIN_ASSERT_ZERO(zeroExpression, ...)
  • zeroExpression==0であることを検証します。
  • WIN_ASSERT_NOT_ZERO(nonzeroExpression, ...)
  • nonzeroExpression!=0であることを検証します。
  • WIN_ASSERT_NULL(nullExpression, ...)
  • nullExpression==NULLであることを検証します。
  • WIN_ASSERT_NOT_NULL(notNullExpression, ...)
  • notNullExpression!=NULLであることを検証します。
  • WIN_ASSERT_FAIL(message, ...)
  • 必ず失敗し、messageを出力します。
  • WIN_ASSERT_TRUE(trueExpression, ...)
  • trueExpressionが真(true,!=0)であることを検証します。
  • WIN_ASSERT_FALSE(falseExpression, ...)
  • falseExpressionが偽(false,==0)であることを検証します。
  • WIN_ASSERT_WINAPI_SUCCESS(trueExpression, ...)
  • trueExpressionが真であることを検証します。偽のときにはGetLastError()の結果を出力します。
  • WIN_ASSERT_THROWS(expression, exceptionType, ...)
  • expressionを評価中にexceptionType型の例外がthrowされることを検証します。

 各WIN_ASSERTの引数に続いてwprintfと同様の書式で失敗時のメッセージを記述できます。例えば、C++標準ライブラリのstd::wstring値を検証するとき、

std::wstring値の検証
BEGIN_TEST(string_test1)
{
  std::wstring actual = L"間違った答";
  WIN_ASSERT_EQUAL(L"正しい答", actual);
}
END_TEST

 の結果は

 WIN_ASSERT_EQUAL failed. Expected: <0x100187A8> ("L"正しい答"");
 Actual: <[OBJECT]> ("actual").

 と出力され、actual値をよみとることができません。そこで:

std::wstring値の検証(拡張引数付き)
BEGIN_TEST(string_test2)
{
  std::wstring actual = L"間違った答";
  WIN_ASSERT_EQUAL(L"正しい答", actual, L"actual=[%s]", actual.c_str());
}
END_TEST

 としておけばactual値を実行結果から確認できます。

WIN_ASSERT_EQUAL failed. Expected: <0x100187A8> ("L"正しい答"");
 Actual: <[OBJECT]> ("actual"). actual=[間違った答]

 冒頭で示したCUnitによるユニットテストをWinUnitで書き換えたものを示します:

WinUnitによるユニットテスト
#include <WinUnit.h>
#include "Car.h"

namespace {
  Car c;
}

// Fixtureの宣言
FIXTURE(car_fixture);

// テスト直前に呼ばれる
SETUP(car_fixture)
{
  c = car_new(50,2);
  WIN_ASSERT_NOT_NULL(c);
}

// テスト直後に呼ばれる
TEARDOWN(car_fixture)
{
  car_delete(c);
}

BEGIN_TESTF(car_init,car_fixture)
{
  WIN_ASSERT_ZERO(car_distance(c));
  WIN_ASSERT_ZERO(car_remain(c));
}
END_TESTF

BEGIN_TESTF(car_refuel,car_fixture)
{
  car_refuel(c, 20);
  WIN_ASSERT_EQUAL(20,car_remain(c));
  car_refuel(c, 20);
  WIN_ASSERT_EQUAL(40,car_remain(c));
  car_refuel(c, 20);
  WIN_ASSERT_EQUAL(50,car_remain(c));
}
END_TESTF

おまけとまとめ

 最後にWinUnitのカスタムロガーを紹介しておきましょう。

 WinUnitは標準出力だけでなく、ユーザーの好みに合わせたログの出力をサポートしています。#include <WinUnitLogger.h>に続いてあらかじめ宣言された関数を実装し、DLLを作ります。

 HTMLを出力する簡単なロガーを書いてみました。

HTMLロガー WinUnit_Logger.cpp
#include <WinUnitLogger.h>
#include <fstream>
#include <locale>

using namespace std ;

wofstream* strm;

/// 初期化
WINUNIT_LOGGER_FUNCTION(void, Initialize)(
    const wchar_t* initializationString  ///< 初期化文字列
)
{
  strm = new wofstream(initializationString);
  strm->imbue(locale("japanese"));
  *strm << L"<html><body>\n";
}

/// テスト前に実行形式名を出力
WINUNIT_LOGGER_FUNCTION(void, OutputTestExecutableNamePreTest)(
    const wchar_t* fullPath,            ///< フルパス
    const wchar_t* nameOfExecutable     ///< 実行形式名
)
{
  *strm << L"実行形式: " << fullPath
        << L"(" << nameOfExecutable << L")<br/>\n"
        << L"<table border='1'><br/>\n";
}

/// テスト後に実行形式名を出力
WINUNIT_LOGGER_FUNCTION(void, OutputTestExecutableNamePostTest)(
    const wchar_t* fullPath,         ///< フルパス
    const wchar_t* nameOfExecutable, ///< 実行形式名
    unsigned int succeeded,          ///< 成功したテスト数
    unsigned int totalRun            ///< 実行したテスト数
)
{
  *strm << L"</table border='1'>\n";
}

const wchar_t* widen(const char* str) {
  static wchar_t buf[512];
  wchar_t* dst = buf;
  while ( *dst++ = (wchar_t)(*str++) ) ;
  return buf;
}

/// テスト前にテスト名を出力
WINUNIT_LOGGER_FUNCTION(void, OutputTestNamePreTest)(
    const char* testName   ///< テスト名
)
{
  *strm << L"<tr><td>" << widen(testName) << L"</td>";
}

/// テスト後にテスト名を出力
WINUNIT_LOGGER_FUNCTION(void, OutputTestNamePostTest)(
    const char* testName,   ///< Name of test that was just run.
    bool passed             ///< True if test passed.
)
{
  *strm << L"<td>"
        << (passed ? L"<font color='green'>成功</font>"
                   : L"<font color='red'>失敗</font>")
        << L"</td></tr>\n";
}

/// トレース出力
/// 今回は省略
/*
WINUNIT_LOGGER_FUNCTION(void, OutputTrace)(
    const wchar_t* message ///< メッセージ
)
{
}
*/

/// テスト失敗時のメッセージ出力
WINUNIT_LOGGER_FUNCTION(void, OutputTestError)(
    const wchar_t* message    ///< 詳細な失敗レポート
)
{
  *strm << L"<td><font color='red'>" << message << L"</font></td>";
}

/// エラー行出力
WINUNIT_LOGGER_FUNCTION(void, OutputProgramErrorLine)(
    const wchar_t* message
)
{
  *strm << L"<td><font color='blue'>" << message << L"</font></td>";
}

/// 最終結果出力
WINUNIT_LOGGER_FUNCTION(void, OutputFinalResults)(
    unsigned int succeeded,  ///< 成功した数
    unsigned int totalRun    ///< テスト総数
)
{
  *strm << L"<font size='+2'>成功したテスト数:" << succeeded
        << L" テスト総数:" << totalRun
        << L"</font><br/></body></html>\n";
  delete strm;
}

/// Verboseレベル
/// やることないのでコメントアウト
/*
WINUNIT_LOGGER_FUNCTION(void, SetVerbosity)(
    unsigned int verbosity
)
{
}
*/

 WinUnit実行時に「-l」オプションに続いてロガーDLL名を与えて実行すると、テスト結果が「result.html」に出力されます

WinUnit -l WinUnit_Logger.dll:result.html byWinUnit.dll
カスタム・ロガーの出力
カスタム・ロガーの出力

 WinUnitはC/C++をターゲットとした単体テストフレームワークの中ではおそらく最も敷居の低いものの一つでしょう。ビギナーの皆さんにはCUnit/CppUnitのライブラリを構築し英文ドキュメントと格闘するのは相当タフな作業と思われ、それがために単体テストをあきらめるのは残念です。WinUnitで単体テストをエンジョイしてください。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
特集記事連載記事一覧

もっと読む

この記事の著者

επιστημη(エピステーメー)

C++に首まで浸かったプログラマ。Microsoft MVP, Visual C++ (2004.01~2018.06) "だった"りわんくま同盟でたまにセッションスピーカやったり中国茶淹れてにわか茶...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/2290 2008/03/18 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー