2013年1月4日金曜日

Windows で eclipse ~CUTE実践編(2)~


続き。

Windows で eclipse ~CUTEインストール編~
Windows で eclipse ~CUTE入門編~
Windows で eclipse ~CUTE入門編 Part.2~
Windows で eclipse ~CUTE実践編(1)~


今回は、CUTEを使ったTDDの実践方法を書く。





……と、ここで驚愕の事実を知った。

「いまどき、C++用のユニットテスティング・フレームワークと言ったら”GoogleTest”ですよ」とのお話を頂いてしまった……。

CUTEの記事を書いている理由は、「Eclipse でC++を使ってTDDがしたい」と思って調べたら、たまたま「CUTE」がHitしてしまったから。

あと、GoogleTestが何者か知らなかったのも原因。嗚呼情弱。(T^T)

まぁ、とにかく始めてしまったので、最後まで続けることにする。
実際、C++でTDDを使って製品プログラムを作る例って少ないと思うので。



■設計

とりあえず設計。
下記仕様に基づき設計してみる。


  • プログラム名は「Calc.exe」とする。
  • 引数に、(1)数字、(2)演算記号、(3)数字の3つのパラメタを入力。演算結果を表示する。
    • パラメタが3以外の場合、「パラメタエラー」と表示。
  • (1)、(3)の数字は、半角10進数8桁とする。
    • 0~9以外の値は、「数字エラー」と表示。
    • 8桁を超える場合は「数字オーバー」と表示。
    • 最大表示桁数は8桁(-99999999~99999999)とし、それ以上の値は「オーバーフロー」と表示。
  • (2)の演算記号は、半角で(A)+、(B)-、(C)/、(D)* の4種とする。
    • +, -, /, * 以外は「演算記号エラー」と表示。       


という訳で、モデル図を作ってみた。

▼ユースケース

  • 人がパラメタ3つを入力。
  • 入力内容を解読し、計算を実行。
  • 結果を表示。


▼クラス図



  • メイン処理をライブラリ[ClacLib]に実装。
  • 外部の[main( )]から呼び出される構造。
  • パラメタを[AplParaChk]にて一元管理。
  • 計算は[AplCalc]にて実施。
  • 上記2つのクラスを制御する[AplMain]を用意。2つのクラスに指示を出し処理を実行。


▼シーケンス図



  • クラス間のメソッドの呼び出しをシーケンス図にまとめた。
  • 異常系は省略。



■開発の進め方

プログラムの実装順序は、「トップダウンアプローチ」と「ボトムアップアプローチ」の2つの方法がある。

<トップダウンアプローチ>
段階的に詳細にしていく方法。
上位から順に、機能の詳細を開発していく。
下位はブラックボックスとして扱うため、下位の設計により上位側に影響を与える可能性がある。

<ボトムアップアプローチ>
システムを構成する個々の部品を開発し、その部品を結合して行き、システム全体を作り上げる。
テストの際、個々の部品を呼び出す「ドライバ」が必須となる。

トップダウントとボトムアップを比較すると、最初に詳細まで設計する「ボトムアップ」の方が品質的にすぐれている。しかし、単体試験を実施するためには、「ドライバ」を作成しなければならず、後に必ず開発するコードとは別にドライバを開発する方が非効率と考えられていた。

このため、トップダウンとボトムアップを混在させたハイブリッド方式が主流となった。具体的には、「正常系を先に作って、詳細を後から作りこむ」といった流れである。

更に、テストは「紙に書いたテスト仕様書」を用いており、コードより前にテスト仕様書を作成すると、コードの変更に伴うテスト仕様書のメンテナンスが発生し、効率が悪かった。

しかし、テスティング・フレームワークの登場で状況は激変する。

テスティング・フレームワークにより完全な「ボトムアップ・アプローチ」が可能となった。更に、TDD(テストが先)により、テスト仕様書より効率的で、確実に動作し、何度も繰り返しテストできる環境が構築可能となったのだ!



……前置きが長くなってしまったが、「完全なボトムアップ開発」と「テスト・ファースト」で開発を進める、と言いたかったのだ。


■最初のメソッド「AplCalc::Add( )」を実装する

今から実施する手順は次の通り。
  1. 手短な設計をする
  2. テストプログラムを作成する。
  3. テストプログラムをテスティングフレームワークに登録する。
  4. テストプログラムをビルドする。 > コンパイルエラーとなる。
  5. 製品コードの雛形を作成する。
  6. テストプログラムをビルド/実行する。 > テストに失敗(赤)する。
  7. 製品コードの中身を実装する。
  8. テストプログラムをビルド/実行する。 > テストに成功(緑)する。
  9. リファクタリングする。
  10. テストプログラムをビルド/実行する。 > テストに成功(緑)する。

▼手短な設計をする


最初に、[AplCalc::Add( )] を作ることにする。
下記仕様とする。

  • 入力値#1、#2 を加算する。
  • 入力値#1、#2は、正負の値が入力される。
  • 足して [-99999999] より小さい または [99999999]より大きい場合、エラー値を返す。
  • エラー値は [0x80000000] とする。


この仕様から考えられるテストパターンを洗い出した。


  • 0 + 0 = 0
  • 1 + 1 = 2
  • 99999998 + 1 = 99999999
  • 1 + 99999998 = 99999999
  • 99999999 + 1 = 0x80000000
  • 1 + 99999999 = 0x80000000
  • 99999999 + 99999999 = 0x80000000
  • 45000000 + (-40000000) = 5000000
  • 40000000 + (-45000000) = -5000000
  • 99999999 + (-99999999) = 0
  • (-99999999) + 99999999 = 0
  • (-45000000) + 40000000 = -5000000
  • (-40000000) + 45000000 = 5000000
  • (-99999999) + (-99999999) = 0x80000000

以上に基づき、開発する。


▼テストプログラムを作成する。
先ずは、[Test.cpp]を書き換える。

  <Test.cpp>

#include "cute.h"
#include "ide_listener.h"
#include "cute_runner.h"
#define RUN_SUITE( name ) runSuite( #name, name );

//-----------------------------------------------------------
void runSuite( char* name, const cute::suite& s ){
    cute::ide_listener lis;
    cute::makeRunner(lis)(s, name);
}

//-----------------------------------------------------------
void runTest(){
    // RUN_SUITE( xxxxxxxxxx() );
}

//-----------------------------------------------------------
int main(){
    runTest();
    return 0;
}


続いて、[AplCalcTest.cpp]を作成する。

  • [プロジェクト・エクスプローラー] > [CalcTest] > [src] を右クリック
  • [新規] > [Suite File] を選択 



  • [Suite name] に [AplCalcTest] と入力し、 [完了] をクリック。




  • [AplCalcTest.h] [AplCalcTest.cpp] が自動生成される。
  <AplCalcTest.h>

#include "cute_suite.h"

extern cute::suite make_suite_AplCalcTest();



  <AplCalcTest.cpp>

#include "cute.h"
#include "ide_listener.h"
#include "cute_runner.h"
#include "AplCalcTest.h"

void thisIsAAplCalcTestTest() {
  ASSERTM("start writing tests", false); }

cute::suite make_suite_AplCalcTest(){
  cute::suite s;
  s.push_back(CUTE(thisIsAAplCalcTestTest));
  return s;
}


[AplCalcTest.cpp.] を書き換える。

  <AplCalcTest.cpp>

#include "cute.h"
#include "ide_listener.h"
#include "cute_runner.h"
#include "AplCalcTest.h"
#include "AplCalc.h"

//-------------------------------------------------------
class AplCalcTest {
public :
};

//-------------------------------------------------------
cute::suite make_suite_AplCalcTest(){
  cute::suite s;
  return s;
}



[AplCalcTest.cpp] にテスト関数 [testAdd( )] を追加する。

  • [AplCalc Cal;] 追加
  • [testAdd( )] 追加
  • [ s.push_back(CUTE_SMEMFUN(AplCalcTest, testAdd)); ] 追加

  <AplCalcTest.cpp>

#include "cute.h"
#include "ide_listener.h"
#include "cute_runner.h"
#include "AplCalcTest.h"
#include "AplCalc.h"

//-------------------------------------------------------
class AplCalcTest {
public :
    AplCalc Cal;

    void testAdd( ){
        ASSERT_EQUAL( (long)Cal.Add(0,0), (long)0 );
        ASSERT_EQUAL( (long)Cal.Add(1,1), (long)2 );
        ASSERT_EQUAL( (long)Cal.Add(99999998,1), (long)99999999 );
        ASSERT_EQUAL( (long)Cal.Add(1,99999998), (long)99999999 );
        ASSERT_EQUAL( (long)Cal.Add(99999999,1), (long)0x80000000 );
        ASSERT_EQUAL( (long)Cal.Add(1,99999999), (long)0x80000000 );
        ASSERT_EQUAL( (long)Cal.Add(99999999,99999999), (long)0x80000000 );
        ASSERT_EQUAL( (long)Cal.Add(45000000,-40000000), (long)5000000 );
        ASSERT_EQUAL( (long)Cal.Add(40000000,-45000000), (long)-5000000 );
        ASSERT_EQUAL( (long)Cal.Add(99999999,-99999999), (long)0 );
        ASSERT_EQUAL( (long)Cal.Add(-99999999,99999999), (long)0 );
        ASSERT_EQUAL( (long)Cal.Add(-45000000,40000000), (long)-5000000 );
        ASSERT_EQUAL( (long)Cal.Add(-40000000,45000000), (long)5000000 );
        ASSERT_EQUAL( (long)Cal.Add(-99999999,-99999999), (long)0x80000000 );
    }
};

//-------------------------------------------------------
cute::suite make_suite_AplCalcTest(){
  cute::suite s;
    s.push_back(CUTE_SMEMFUN(AplCalcTest, testAdd)); 
return s;
}

以上で完了。



▼テストプログラムをテスティングフレームワークに登録する。


[Test.cpp.] に、 [AplCalcTest] を追加する。
  • [#include "AplCalcTest.h"] 追加
  • [RUN_SUITE( make_suite_AplCalcTest() );] 追加
  <Test.cpp>

    #include "cute.h"
    #include "ide_listener.h"
    #include "cute_runner.h"
    #include "AplCalcTest.h"
    #define RUN_SUITE( name ) runSuite( #name, name );

    //-----------------------------------------------------------
    void runSuite( char* name, const cute::suite& s ){
        cute::ide_listener lis;
        cute::makeRunner(lis)(s, name);
    }

    //-----------------------------------------------------------
    void runTest(){
    // RUN_SUITE( xxxxxxxxxx() );
        RUN_SUITE( make_suite_AplCalcTest() );
    }
    ----------------------------------------------------------
    int main(){
        runTest();
        return 0;
    }



    ▼テストプログラムをビルドする。 > コンパイルエラーとなる。

    テスト関数が実装されたかどうか確認するため、ビルドエラーとなる事を確認する。

    • [Ctrl+B] を押下。
    • [AplCalc] が無いため、ビルドエラーとなる。



    ▼製品コードの雛形を作成する。

    製品コードが組み込まれたか確認するため、中身を実装せず雛形だけ追加する。

    • [プロジェクト・エクスプローラー] > [CalcLib] を右クリック
    • [新規] > [クラス] を選択 



    • [名前空間] のチェックをOFF
    • [クラス名] に 「AplCalc」 を入力
    • [完了] をクリック



    [AplCalc.h] [AplCalc.cpp] が自動生成される。


    [long Add( long data1, long data2 );]を追加する。
    ただし、関数の中身は [return 0;] だけとする。



    ▼テストプログラムをビルド/実行する。 > テストに失敗(赤)する。

    製品コードの雛形が組み込まれたことを確認するため、ビルドとテストを実行する。
    コードが組み込まれていないため、当然テストは失敗する。
    • ビルドが正常終了することを確認



    • テストが失敗する事を確認



    ▼製品コードの中身を実装する。

    仕様通り動作する様実装する。



    ▼テストプログラムをビルド/実行する。 > テストに成功(緑)する。

    テストをビルド/実行して、テストが成功する事を確認する。



    ▼リファクタリングする。

    コードを見直し、下記修正を実施。

    • 直値を#define化
    • 条件判定文が分かりずらいので、関数化



    ▼テストプログラムをビルド/実行する。 > テストに成功(緑)する。


    再度ビルド/実行して、テストが成功する(=デグレしていない)事を確認する。




    以上で、ひととおりTDDができた。
    次回は残りを作っていく。

      0 件のコメント:

      コメントを投稿