.. include:: extlink.txt ==================== テスト駆動開発 (TDD) ==================== ワークフロー ============ TDDのワークフローは `[Gre00]`_ で詳細に説明されているので、そちらを参照のこと。 テスティングフレームワーク ========================== `[Gre00]`_ ではテスティングフレームワーク [#f1]_ に `Unity `_ および `CppUTest `_ が紹介されているが、特に実機でユニットテストを 実行したいという要求がない限り、本ドキュメントは `GoogleTest `_ [#f2]_ の使用を推奨する。以下、GoogleTest の使用を推奨する理由を述べる。 アサーション (Assertion) GoogleTest では、 `JUnit `_ 由来の、アサーション ASSERT_THAT() を使用することができる。 ASSERT_THAT() は、以下に示す通り比較的自然言語に近い形で表記することができる。 ここに示す以外にも、豊富なマッチャーが用意されているので、 適宜 `本家の情報 `_ を参照のこと。 .. code-block:: c++ #include "gtest/gtest.h" #include "gmock/gmock.h" ... using ::testing::Eq; using ::testing::Gt; TEST(ClassA, Func_returns_0) { ClassA classA; ASSERT_THAT(classA.Func(), Eq(0)); // classA.Func() == 0 } TEST(ClassB, Func_returns_greater_than_5) { ClassB classB; ASSERT_THAT(classB.Func(), Gt(5)); // classB.Func() > 5 } 値をパラメータ化したテスト (Value Parameterized Tests) 境界値テストを実施する場合、以下のアプローチを取ることが一般的である。 1. パラメータのみ変えたテスト関数を複数用意する 2. ひとつのテスト関数に複数のテストをまとめる しかしながら、これらのようなアプローチをとった場合、1点目はテストコードの冗長化を招き、 2点目はテスト関数の意図が不明確になるデメリットがある。 これに対し、GoogleTest には「値をパラメータ化したテスト」と呼ばれる機能があり、ひとつの テスト関数で複数のテストを実行することができる。 まず、 `[Gre00]`_ で紹介されている LedDriver クラスのテストコード (CppUTest版) を示す。 .. code-block:: c++ TEST(LedDriver, OutOfBoundsTurnOffDoesNoHarm) { LedDriver_TurnAllOn(); LedDriver_TurnOff(-1); LedDriver_TurnOff(0); LedDriver_TurnOff(17); LedDriver_TurnOff(3141); LONGS_EQUAL(0xffff, virtualLeds); } 上記のテスト関数は、範囲 (1~16) 外のIDを与えた場合はLEDの状態が変更されないことを テストするものであるが、4回の LedDriver_TurnOff() の呼び出し中に、LEDが意図しない 状態となっていないことを保証することができない。 次に、上記関数を GoogleTest に移植したテストコードを示す。 .. code-block:: c++ #include "gtest/gtest.h" #include "gmock/gmock.h" ... using ::testing::Eq; class LedDriverTest : public ::testing::Test { protected: ... }; class all_leds_turn_off : public LedDriverTest {}; class arg_pattern_test_when_all_leds_turn_off : public all_leds_turn_off, public ::testing::WithParamInterface {}; TEST_P(arg_pattern_test_when_all_leds_turn_off, TurnOn_does_no_harm) { LedDriver_TurnAllOn(); const int n = GetParam(); // RuntimeErrorモックに対し、期待する振る舞いを指定 // 本説明では不要であるため省略 LedDriver_TurnOff(n); ASSERT_THAT(virtualLeds_, Eq(0xffffu)); } INSTANTIATE_TEST_CASE_P( aPatternInstance, arg_pattern_test_when_all_leds_turn_off, ::testing::Values(-1, 0u, 17u, 3141u)); 記述の詳細は本家ドキュメントに譲るが、GoogleTest 版では、上記のようにテスト関数を 実装することで、1パラメータごとにテストを実行することができ、LEDが意図しない状態に なっていないことを確実に保証することができる。 その他、組み合わせテストのパラメータ生成に非常に有効な Combine() というパラメータ 生成関数が用意されている。 GoogleMock `[Gre00]`_ ではテストダブルを実装するための手法が複数紹介されているが、GoogleTest の拡張 フレームワーク GoogleMock を使用すれば同等以上のテストが実施できる。 ここまでに紹介した GoogleTest の利点をより読者に感じていただくため、 `[Gre00]`_ の `サイト `_ で配布されているコードを GoogleTest に 移植した。 `GitHub `_ で公開している ので、ぜひ一読いただきたい。 ユニットテストコード ==================== 以下を参考に、プロジェクトごとにルールを定めることを推奨する。 コーディングルール ^^^^^^^^^^^^^^^^^^ 原則 `Google C++ Style Guide `_ を基本とする。 ただし、C++11/14で追加された機能に対する制限はしないものとする。 また、命名規則は一部変更する。 メソッド (メンバ関数) * | public、protected | パスカルケース * | private | キャメルケース フィールド (メンバ変数) * | public | プレフィックス「k」でパスカルケース、最後にアンダーバー | ※定数(const、constexpr)以外の宣言は禁止する。 * | protected | パスカルケースで最後にアンダーバー * | private | キャメルケースで最後にアンダーバー 構造 ^^^^ .. figure:: ./img/structure-of-ut-code.png :width: 100% :align: center ユニットテスト構造の例 .. rubric:: 脚注 .. [#f1] `[Gre00]`_ ではテストハーネスと表現されている。 .. [#f2] 本ドキュメントでは、GoogleMock も含めて GoogleTest と呼ぶこととする。