SlideShare a Scribd company logo
1 of 24
Download to read offline
プログラマのためのテスト
         Kuniaki IGARASHI
 http://igarashikuniaki.net/tdiary/
    igarashikuniaki@gmail.com
             2007.4.24
UnitTest入門と
UnitTestデザインパターン
UnitTest
                      ユニットテスト


メソッド単位でコードを検証するテストコードを書き、
戻り値、副作用が妥当であることを確認するテスト
               UnitTest(C++/CPPUnit)のCode例
// テスト対象メソッド addition(int arg1, int arg2)
// 引数の和を返し、メンバ変数m_lastResultに結果を格納するメソッド

int result = addition(2,3); // テスト対象のメソッドを実行して

CPPUNIT_ASSERT_EQUAL((int) 5, result);          // 結果を確認
CPPUNIT_ASSERT_EQUAL((int) 5, m_lastResult);    // 結果を確認


    Green テスト成功                                Red テスト失敗
UnitTestの基本方針
• 確実にNGになるケースをまず書こう
 テストが実行されていることを確かめましょう                 Red テスト失敗
 例:CPPUNIT_FAIL(quot;messagequot;); /// 必ず失敗し、messageを表示するテスト
 テストできたらCPPUNIT_FAILを外してGreenにします。 Green テスト成功
• テストを書こう
 新規実装であれば仕様をコードに落としながら。
 既存実装の修正
 1.   既存のコードに合致するテストを書く。     Green テスト成功
 2.   修正後の仕様を満たすテストを書く。       Red テスト失敗


• 本番コードを書こう
 テストがGreenになるように本番コードを書いていきます。 Green テスト成功
 テストで仕様抜け、テストコードパス抜けを見つけたらテストを書き足します。
• リファクタリング
 必要があれば、テストを成功のまま保ちつつリファクタリングを行います。
                                                        Green テスト成功
テスト基本形
// テスト対象メソッド Calc::addition(int arg1, int arg2)
// 引数の和を返し、メンバ変数m_lastResultに結果を格納するメソッド
void CalcUnitTest::test_Addition(void)
{
   Calc calc;                                   // テスト対象クラスを作成
   int result = calc.addition(2,3);             // テスト対象のメソッドを実行して

    CPPUNIT_ASSERT_EQUAL((int) 5, result);            // 結果を確認
    CPPUNIT_ASSERT_EQUAL((int) 5, calc.m_lastResult); // 結果を確認
    // ↑calc.m_lastResultにテストクラスからアクセスできるように、
    // テスト対象クラスのヘッダファイルで以下のようにfriend指定しておきます。
    // friend class FrameBuilderUnitTest;
}


        CPPUNIT_ASSERT_EQUAL (x,y) → x==y の場合にOK。
        CPPUNIT_ASSERT_EQUAL((int) 期待値, テスト対象値);
        通常、期待値を第1引数に、テスト対象値を第2引数に渡します。
        型をCPPUnitに伝えるために、明示的にキャストする必要があります。
例外送出のテスト
/// テスト対象 Yarinage::Set(Yari yari)
/// 槍をセットされると槍型の例外を投げるメソッド。
 void YarinageUnitTest::test_Set(void)
{                                                     CPPUNIT_ASSERT_THROW(実行,送出予期型);
  Yarinage yarinage; // テスト対象クラスを作成                   CPPUNIT_ASSERT_NO_THROW();
  Yari yari;         // テストに使うクラスを作成

    CPPUNIT_ASSERT_THROW(yarinage.Set(yari), Yari);
    // 飛ばないことを確認するには
    // CPPUNIT_ASSERT_NO_THROW();                     void YarinageUnitTest::test_Set(void)
}                                                     {
                                                        Yarinage yarinage;
                                                        Yari yari;

                                                          try{
                                                             yarinage.Set(yari);

                                                             // NGパターン
                                                             CPPUNIT_FAIL(quot;何も飛んできませんでしたよ。quot;);
                                                          } catch (Yari e) {
                                                             // OKパターン 槍が投げられました。
            または、特別なことをせず                                  } catch (...){
            意図しないコードパスにCPPUNIT_FAILを                         // NGパターン
                                                             CPPUNIT_FAIL(quot;槍じゃないものが飛んできましたよ。quot;);
            入れてNGにすれば良い。                                  }
                                                      }
全パス・全状態の網羅

直交表や全ペアといったテスト技法が助けになります
    →参考文献:「はじめて学ぶソフトウェアのテスト技法」




                           直行表
Mock(Stub)
          制御可能なニセモノ下位モジュール

   ホンモノコード                              テストコード

                     テスト対象モジュール
         Calc::Calc(){ // コンストラクタ
         #ifndef UNITTEST
            m_paiCalc = new PaiCalc(); // ホンモノ
         #else
            m_paiCalc = new PaiCalcMock(); // ニセモノ
         #endif
         }

     PaiCalc                        PaiCalcMock
 GetResult (1000000)             GetResult (1000000)
5分計算して結果を返す                   てきとーな値ですぐに結果を返す
                              返す値はテストコードから制御可能
  C++ヘッダファイルからMockを生成するMockMaker作りました。
  (C++ヘッダのパースがまだまだ全然ダメですが)
  http://igarashikuniaki.net/data/MockMaker/MockMaker.zip
メソッド呼び出しの確認
テスト対象モジュール
                                        テストコード
Foo::DoCalcPai()
                       g_is_ PaiCalcMock_GetResult _Invoked = false;
内で                     foo.DoCalcPai(); // テスト実行
PaiCalc::GetResult()   CPPUNIT_ASSERT_EQUAL((bool)true,
が呼び出されるか                 g_is_ PaiCalcMock_GetResult _Invoked);
確認するテスト
                                Foo(テスト対象モジュール)
                       Foo::DoCalcPai() {
                         m_paiCalc->GetResult (1000000);
                                // Test時はMockが呼び出される
                       }

                                        PaiCalcMock
                       PaiCalcMock::GetResult (1000000){
                         g_is_ PaiCalcMock_GetResult _Invoked = true
テスタで導通チェックを              // メソッド内でグローバル変数に値代入
するようなものです。             }
UnitTest実装時のCheckList
•   全てのパスを通過
•   全ての環境(変数やメモリ状態)の組み合わせ
•   全ての異常系
•   全ての状態遷移
•   例外送出
•   NULLチェック
•   境界値
•   変換 - 逆変換 - diff
•   Initializeが呼ばれる前に全メソッドを呼ぶテスト
•   Finalizeが呼ばれた後に全メソッドを呼ぶテスト
•   Initializeを呼んだ後にInitializeを呼ぶテスト
Test First Development
Test Driven Development
  本番コードを書く前にユニットテストコードを書く
  → テストコードに駆動される開発

テストコードで境界値や全てのケースを網羅しようという意識になる
     仕様が固まっていない部分は固めようとする
      テスト容易な設計を考慮するようになる

      テストを満たすように、突貫コードを書く
        仕様破綻がないかチェックする

       エラーケースをケアするコードを書く
           リファクタリング
テスト容易な設計とはなんだ?
Mockオブジェクトをどうやって置き換えるか?

• #ifdef (前述)   Foo::Foo(){ // コンストラクタ
                #ifndef UNITTEST
                   m_bar = new Real(); // ホンモノ
                #else
                   m_bar = new Mock(); // ニセモノ
                #endif
                }


• DI(Dependency Injection:依存性の注入)
  (例: GoFのストラテジーdesign patternを利用)
Dependency Injectionで
             Real/Mock切りかえ
               RealBar
  SetFoo(InterfaceFoo* foo){             InterfaceFoo
    m_foo = foo;                     Calc() 純粋仮想関数
  }

  m_foo->Calc();                    RealFoo       MockFoo
                                   Calc()を実装     Calc()を実装
  // 本番時はRealFoo
  // テスト時はMockFoo

             本番コード                           テストコード
    RealMain(){                    TestMain(){
      RealBar bar;                   RealBar bar;
      bar.SetFoo(new RealFoo());     bar.SetFoo(new McokFoo());
      ...                            ...
    }                              }


本番コードにテストコードが含まれないスッキリした設計・実装!
テスト容易な設計とはなんだ?

• 適切なモジュール化

テストを書きづらいコードはかかない
こんなメソッドは嫌だ
• 仕事内容が多岐
• 1000行
サンプルコード
==== DI と Mock Object を利用した UnitTest Pattern ====   ■テストコード
                                                    // 実際は Mockpp を利用して、より柔軟にテスト条件に応じた値を返す Mock を作る
■ 本番コード                                             class MockFoo : public IFoo {
Bar class は Foo class の演算結果を利用して処理をします。                int m_calc_value;

class IFoo : public IFoo {                                void SetCalcFoo(int value) {
  int CalcFoo();                                            m_calc_value = value;
}                                                         }                              Mockpp を利用することで尐し楽をして Mock class を作成できる
                                                                                         ようになります。
class RealFoo {                                         int CalcFoo() {
  int CalcFoo() {                                         return m_calc_value;           このようにして Mock Class を一度作っておけば、テスト本体だけで
    return 3;                                           }                                テスト条件を記述できるようになり、テスト時も本番時もまったく
  }                                                 }                                    同じオブジェクトを利用できるようになります。
}                                                                                        # リビルド不要、本番とテストでコードの食い違いが起こりえない
                                                    void test_main()
class RealBar                                       {                                    また、必ず依存関係の間にインタフェースが挟まるので、
{                                                     MockFoo foo;                       それぞれのクラスを別々に開発することも容易になります。
  IFoo *m_foo;                                        RealBar bar;                       # 依存先の実体がなくてもコンパイルできるし、Mock を入れて
                                                                                         # 逐次テストができる
    void SetFoo(IFoo* foo) {                            bar.SetFoo(&foo);
      m_foo = foo;                                                                       つまり、DI + MockObject で UnitTest というのは、このような
    }                                                   foo.SetCalcValue(1);             メリットを得るために、本番コードのアーキテクチャを変えるという
                                                        ASSERT(bar.CalcBar() == 2);      extreme なテスト手法です。
      // CalcFoo() の結果を 2倍する
    int CalcBar() {                                     foo.SetCalcValue(2);
      return 2 * m_foo->CalcFoo();                      ASSERT(bar.CalcBar() == 4);
    }                                               }
}

void main()
{
  RealFoo foo;
  RealBar bar;

    // Setter Injection.
    bar.SetFoo(&foo);

}
    printf(quot;CalcBar = %d¥nquot;, bar.CalcBar());                                                                   (c)ひぐまさん
                                                                                                               多謝!!
Javaの世界は一歩先を行く
• Seasar2 (DIコンテナFrameWork)
コンテナの中身を外部ファイルで自由に操作
                                    .xml
コード
使用したいコンテナ(クラ
                                    aのインスタンスは○○
ス)の名前だけしっていれ
                                    bのインスタンスは△△
ばいい。
                                    cのインスタンスは□□
そのインスタンスが何か                         ...
は意識しなくて良い。
                     DIコンテナ

     本番・テストごとにxmlを切り替えてReal/Mockを指定する。
  Real.xml         Mock.xml           TestA.xml
  aのインスタンスはRealA   aのインスタンスはMockA     aのインスタンスはRealA
  bのインスタンスはRealB   bのインスタンスはMockB     bのインスタンスはMockB
  cのインスタンスはRealC   cのインスタンスはMockC     cのインスタンスはMockC
UnitTestPatterns
                                                                   ■The Simple-Test Pattern(シンプルテストパターン)
どのようなUnitTestを書け                                                   ■The Code-Path Pattern(コードパスパターン)
                                                                   ■The Parameter-Range Pattern(パラメータレンジパターン)

ばいいのかを形式化し、テ                                                       ■Data Driven Test Patterns(データ駆動テストパターン)
                                                                   ■The Simple-Test-Data Pattern(シンプルテストデータパターン)

ストファーストをプログラマ                                                      ■The Data-Transformation-Test Pattern(データ変換テストパターン)
                                                                   ■Data Transaction Patterns (データトランザクションパターン)

の習慣にすることを目指す                                                       ■The Simple-Data-I/O Pattern(シンプルデータI/Oパターン)
                                                                   ■The Constraint-Data Pattern(制約データパターン)
                                                                   ■The Rollback Pattern(ロールバックパターン)
webページ                                                             ■Collection Management Patterns(コレクション管理パターン)
                                                                   ■The Collection-Order Pattern(コレクションオーダリングパターン)
http://www.marcclifton.com/tabid/87/Default.aspx (英語)              ■The Enumeration Pattern(列挙パターン)
                                                                   ■The Collection-Constraint Pattern(コレクション制約パターン)
http://igarashikuniaki.net/fswiki/wiki.cgi?page=UnitTestPatterns
                                                                   ■The Collection-Indexing Pattern(コレクションインデクシングパターン)
           (日本語翻訳途中)
                                                                   ■Performance Patterns(パフォーマンスパターン)
                                                                   ■The Performance-Test Pattern(パフォーマンステストパターン)
                                                                   ■Process Patterns(プロセスパターン)
                                                                   ■The Process-Sequence Pattern(プロセスシーケンスパターン)
                                                                   ■The Process-State Pattern(プロセス状態パターン)
                                                                   ■The Process-Rule Pattern(プロセスルールパターン)
                                                                   ■Simulation Patterns(シミュレーションパターン)
                                                                   ■Mock-Object Pattern(モックオブジェクトパターン)
                                                                   ■The Service-Simulation Pattern(サービスシミュレーションパターン)
                                                                   ■The Bit-Error-Simulation Pattern(ビットエラーシミュレーションパターン)


                        テストしやすい設計をするために、テストファースト。
                        テストを設計前に書けば、テストまで考慮した設計が可能。
UnitTestPatterns
テストを育てる
  テストが書かれている範囲はコントロール
  下にあるということ。

  テストを追加していくということは、
  テストにより動作保証される範囲を増やし、
  リスク把握できる部分を広げていくこと。
                     ※NGでひっかかった回数
          OK         を記録するような仕組みが
                     あると面白いね。
         OK
               OK

現在、テストがカバーしているコードがどの辺なのか        バグの場所が分かる
見える化してくれるツールがあるといいですね。          ドラゴンレーダーが欲しいです。
UnitTestツール
 xUnit
 •JUnit (JAVA)
 •NUnit (C#)
 •CppUnit (C++)
どの環境でもユニットテストツールは大抵あります。



       CppUnit動作環境
         VC++, g++(GCC)など。
UnitTestのまとめ
• テストにより駆動される開発
 – 気づけなかったものに気づく
 – 発想を広げる
• テスト容易性まで考慮した設計
 – モジュール化重要
• テストを用いたリスク解析
 テスト△はOKなので、△の範囲は問題無し
 テスト□がNGだったので、□の範囲内に問題有り
参考文献
•   はじめて学ぶソフトウェアのテスト技法
    著:リー・コープランド ISBN : 4-8222-8251-1
    これ1冊でプログラマレベルではテストマスター
•   CPPUnitによる実践テスト技法
       http://www.mikamama.com/CppUnitBook/draft/index.html (draft)
    著:大月美佳 ISBN:4-7980-0571-1
    ユニットテストの入門書。リファレンスとしても便利。
•   知識ゼロから学ぶソフトウェアテスト
    著:高橋寿一 ISBN:4-7981-0709-3
    SONYのテストエンジニアさんの著書。
    実際の経験に基づくノウハウが多数。
•   WEB+DB PRESS vol.35
    著:和田卓人 ISBN:4-7741-2931-3
    Java DIコンテナSeasar2を使ったユニットテストの記事など入門記事。
おしまい




質問があればお願いします。
テストのゴールはいつだ?

 テストにかかった費用
      +
 サポートにかかる費用
      +
メンテナンスにかかる費用

  が最小値になるとき

More Related Content

What's hot

Abacus tamil (Sun Easy Abacus)
Abacus tamil (Sun Easy Abacus)Abacus tamil (Sun Easy Abacus)
Abacus tamil (Sun Easy Abacus)R Sunder Raj
 
「レガシーコード」とはいったい?
「レガシーコード」とはいったい?「レガシーコード」とはいったい?
「レガシーコード」とはいったい?Hiroyuki Ohnaka
 
はじめてのパターン認識 p.159~177
はじめてのパターン認識 p.159~177はじめてのパターン認識 p.159~177
はじめてのパターン認識 p.159~177ssuser74dafc
 
宛 課程
宛 課程宛 課程
宛 課程clinic
 
iReport實做練習part(1)
iReport實做練習part(1) iReport實做練習part(1)
iReport實做練習part(1) seatof
 
Ohp Seijoen H20 01 Programming No Nagare
Ohp Seijoen H20 01 Programming No NagareOhp Seijoen H20 01 Programming No Nagare
Ohp Seijoen H20 01 Programming No Nagaresesejun
 
پانی کے بارے میں اہم معلومات
پانی کے بارے میں اہم معلوماتپانی کے بارے میں اہم معلومات
پانی کے بارے میں اہم معلوماتIlyas Qadri Ziaee
 
دعوت اسلامی کا تعارف
دعوت اسلامی کا تعارفدعوت اسلامی کا تعارف
دعوت اسلامی کا تعارفdawateislami
 
Al Fazl International = Weekly 3 October 2014
Al Fazl International = Weekly 3 October 2014Al Fazl International = Weekly 3 October 2014
Al Fazl International = Weekly 3 October 2014muzaffertahir9
 
Esg Ds500 Mixed Workload Results
Esg Ds500 Mixed Workload ResultsEsg Ds500 Mixed Workload Results
Esg Ds500 Mixed Workload Resultschowhf
 
Trigonometry workbook for dummies m. sterling (wiley, 2005)
Trigonometry workbook for dummies   m. sterling (wiley, 2005)Trigonometry workbook for dummies   m. sterling (wiley, 2005)
Trigonometry workbook for dummies m. sterling (wiley, 2005)Oswaldo Perez Mayet
 
CEO-004-Wild Bird Life
CEO-004-Wild Bird LifeCEO-004-Wild Bird Life
CEO-004-Wild Bird Lifehandbook
 
التقرير المالي السنوي - سنة 2017
التقرير المالي السنوي - سنة 2017التقرير المالي السنوي - سنة 2017
التقرير المالي السنوي - سنة 2017ahli bank
 
التقرير المالي السنوي - سنة 2016
التقرير المالي السنوي - سنة 2016التقرير المالي السنوي - سنة 2016
التقرير المالي السنوي - سنة 2016ahli bank
 

What's hot (20)

Abacus tamil (Sun Easy Abacus)
Abacus tamil (Sun Easy Abacus)Abacus tamil (Sun Easy Abacus)
Abacus tamil (Sun Easy Abacus)
 
「レガシーコード」とはいったい?
「レガシーコード」とはいったい?「レガシーコード」とはいったい?
「レガシーコード」とはいったい?
 
德勤工作手册
德勤工作手册德勤工作手册
德勤工作手册
 
ilm ul Qur'aan
ilm ul Qur'aanilm ul Qur'aan
ilm ul Qur'aan
 
はじめてのパターン認識 p.159~177
はじめてのパターン認識 p.159~177はじめてのパターン認識 p.159~177
はじめてのパターン認識 p.159~177
 
宛 課程
宛 課程宛 課程
宛 課程
 
Intro. to JavaScript
Intro. to JavaScriptIntro. to JavaScript
Intro. to JavaScript
 
iReport實做練習part(1)
iReport實做練習part(1) iReport實做練習part(1)
iReport實做練習part(1)
 
Ohp Seijoen H20 01 Programming No Nagare
Ohp Seijoen H20 01 Programming No NagareOhp Seijoen H20 01 Programming No Nagare
Ohp Seijoen H20 01 Programming No Nagare
 
b2con2006_sato
b2con2006_satob2con2006_sato
b2con2006_sato
 
2008 花東三天兩日遊 計劃書 導覽手冊
2008 花東三天兩日遊 計劃書 導覽手冊2008 花東三天兩日遊 計劃書 導覽手冊
2008 花東三天兩日遊 計劃書 導覽手冊
 
پانی کے بارے میں اہم معلومات
پانی کے بارے میں اہم معلوماتپانی کے بارے میں اہم معلومات
پانی کے بارے میں اہم معلومات
 
دعوت اسلامی کا تعارف
دعوت اسلامی کا تعارفدعوت اسلامی کا تعارف
دعوت اسلامی کا تعارف
 
Al Fazl International = Weekly 3 October 2014
Al Fazl International = Weekly 3 October 2014Al Fazl International = Weekly 3 October 2014
Al Fazl International = Weekly 3 October 2014
 
Esg Ds500 Mixed Workload Results
Esg Ds500 Mixed Workload ResultsEsg Ds500 Mixed Workload Results
Esg Ds500 Mixed Workload Results
 
Trigonometry workbook for dummies m. sterling (wiley, 2005)
Trigonometry workbook for dummies   m. sterling (wiley, 2005)Trigonometry workbook for dummies   m. sterling (wiley, 2005)
Trigonometry workbook for dummies m. sterling (wiley, 2005)
 
CEO-004-Wild Bird Life
CEO-004-Wild Bird LifeCEO-004-Wild Bird Life
CEO-004-Wild Bird Life
 
التقرير المالي السنوي - سنة 2017
التقرير المالي السنوي - سنة 2017التقرير المالي السنوي - سنة 2017
التقرير المالي السنوي - سنة 2017
 
Soalan tahun6 part2
Soalan tahun6 part2Soalan tahun6 part2
Soalan tahun6 part2
 
التقرير المالي السنوي - سنة 2016
التقرير المالي السنوي - سنة 2016التقرير المالي السنوي - سنة 2016
التقرير المالي السنوي - سنة 2016
 

More from Kuniaki Igarashi

my_everyday_life_with_ruby
my_everyday_life_with_rubymy_everyday_life_with_ruby
my_everyday_life_with_rubyKuniaki Igarashi
 
EM高専カンファレンス 編集後記とまえがき
EM高専カンファレンス 編集後記とまえがきEM高専カンファレンス 編集後記とまえがき
EM高専カンファレンス 編集後記とまえがきKuniaki Igarashi
 
世界のどこかで楽しくRubyでお仕事するために
世界のどこかで楽しくRubyでお仕事するために世界のどこかで楽しくRubyでお仕事するために
世界のどこかで楽しくRubyでお仕事するためにKuniaki Igarashi
 
「学生さんと一緒に Rubyイベントを つくっている←イマココ 」
「学生さんと一緒に Rubyイベントを つくっている←イマココ 」「学生さんと一緒に Rubyイベントを つくっている←イマココ 」
「学生さんと一緒に Rubyイベントを つくっている←イマココ 」Kuniaki Igarashi
 
高専カンファレンスカンファレンス
高専カンファレンスカンファレンス高専カンファレンスカンファレンス
高専カンファレンスカンファレンスKuniaki Igarashi
 
高専カンファレンス014Tokyo 前説
高専カンファレンス014Tokyo 前説高専カンファレンス014Tokyo 前説
高専カンファレンス014Tokyo 前説Kuniaki Igarashi
 
高専カンファレンス014tokyo 実行委員長挨拶
高専カンファレンス014tokyo 実行委員長挨拶高専カンファレンス014tokyo 実行委員長挨拶
高専カンファレンス014tokyo 実行委員長挨拶Kuniaki Igarashi
 
雷は落ちるのか昇るのか
雷は落ちるのか昇るのか雷は落ちるのか昇るのか
雷は落ちるのか昇るのかKuniaki Igarashi
 
World Wide Ruby Conferences
World Wide Ruby ConferencesWorld Wide Ruby Conferences
World Wide Ruby ConferencesKuniaki Igarashi
 
高専カンファレンス 014東京 予告編
高専カンファレンス 014東京 予告編高専カンファレンス 014東京 予告編
高専カンファレンス 014東京 予告編Kuniaki Igarashi
 
高専カンファレンス2010秋 予告編
高専カンファレンス2010秋 予告編高専カンファレンス2010秋 予告編
高専カンファレンス2010秋 予告編Kuniaki Igarashi
 
高専カンファレンスについて @kosenconf-012hachinohe
高専カンファレンスについて @kosenconf-012hachinohe高専カンファレンスについて @kosenconf-012hachinohe
高専カンファレンスについて @kosenconf-012hachinoheKuniaki Igarashi
 
高専カンファレンス2009秋in東京 実行委員長挨拶
高専カンファレンス2009秋in東京 実行委員長挨拶高専カンファレンス2009秋in東京 実行委員長挨拶
高専カンファレンス2009秋in東京 実行委員長挨拶Kuniaki Igarashi
 
高専カンファレンス2009秋in東京 予告編
高専カンファレンス2009秋in東京 予告編高専カンファレンス2009秋in東京 予告編
高専カンファレンス2009秋in東京 予告編Kuniaki Igarashi
 
RubyKaigi Lightning Talks TwYM episode2
RubyKaigi Lightning Talks TwYM episode2RubyKaigi Lightning Talks TwYM episode2
RubyKaigi Lightning Talks TwYM episode2Kuniaki Igarashi
 
RubyKaigi Lightning Talks TwYM episode1
RubyKaigi Lightning Talks TwYM episode1RubyKaigi Lightning Talks TwYM episode1
RubyKaigi Lightning Talks TwYM episode1Kuniaki Igarashi
 
197x Timer with Your Messages
197x Timer with Your Messages197x Timer with Your Messages
197x Timer with Your MessagesKuniaki Igarashi
 

More from Kuniaki Igarashi (20)

my_everyday_life_with_ruby
my_everyday_life_with_rubymy_everyday_life_with_ruby
my_everyday_life_with_ruby
 
EM高専カンファレンス 編集後記とまえがき
EM高専カンファレンス 編集後記とまえがきEM高専カンファレンス 編集後記とまえがき
EM高専カンファレンス 編集後記とまえがき
 
世界のどこかで楽しくRubyでお仕事するために
世界のどこかで楽しくRubyでお仕事するために世界のどこかで楽しくRubyでお仕事するために
世界のどこかで楽しくRubyでお仕事するために
 
「学生さんと一緒に Rubyイベントを つくっている←イマココ 」
「学生さんと一緒に Rubyイベントを つくっている←イマココ 」「学生さんと一緒に Rubyイベントを つくっている←イマココ 」
「学生さんと一緒に Rubyイベントを つくっている←イマココ 」
 
高専カンファレンスカンファレンス
高専カンファレンスカンファレンス高専カンファレンスカンファレンス
高専カンファレンスカンファレンス
 
高専カンファレンス014Tokyo 前説
高専カンファレンス014Tokyo 前説高専カンファレンス014Tokyo 前説
高専カンファレンス014Tokyo 前説
 
高専カンファレンス014tokyo 実行委員長挨拶
高専カンファレンス014tokyo 実行委員長挨拶高専カンファレンス014tokyo 実行委員長挨拶
高専カンファレンス014tokyo 実行委員長挨拶
 
雷は落ちるのか昇るのか
雷は落ちるのか昇るのか雷は落ちるのか昇るのか
雷は落ちるのか昇るのか
 
World Wide Ruby Conferences
World Wide Ruby ConferencesWorld Wide Ruby Conferences
World Wide Ruby Conferences
 
高専カンファレンス 014東京 予告編
高専カンファレンス 014東京 予告編高専カンファレンス 014東京 予告編
高専カンファレンス 014東京 予告編
 
高専カンファレンス2010秋 予告編
高専カンファレンス2010秋 予告編高専カンファレンス2010秋 予告編
高専カンファレンス2010秋 予告編
 
Cafe igaiga
Cafe igaigaCafe igaiga
Cafe igaiga
 
高専カンファレンスについて @kosenconf-012hachinohe
高専カンファレンスについて @kosenconf-012hachinohe高専カンファレンスについて @kosenconf-012hachinohe
高専カンファレンスについて @kosenconf-012hachinohe
 
高専カンファレンス2009秋in東京 実行委員長挨拶
高専カンファレンス2009秋in東京 実行委員長挨拶高専カンファレンス2009秋in東京 実行委員長挨拶
高専カンファレンス2009秋in東京 実行委員長挨拶
 
勉強会でAgileを
勉強会でAgileを勉強会でAgileを
勉強会でAgileを
 
高専カンファレンス2009秋in東京 予告編
高専カンファレンス2009秋in東京 予告編高専カンファレンス2009秋in東京 予告編
高専カンファレンス2009秋in東京 予告編
 
RubyKaigi Lightning Talks TwYM episode2
RubyKaigi Lightning Talks TwYM episode2RubyKaigi Lightning Talks TwYM episode2
RubyKaigi Lightning Talks TwYM episode2
 
RubyKaigi Lightning Talks TwYM episode1
RubyKaigi Lightning Talks TwYM episode1RubyKaigi Lightning Talks TwYM episode1
RubyKaigi Lightning Talks TwYM episode1
 
197x Timer with Your Messages
197x Timer with Your Messages197x Timer with Your Messages
197x Timer with Your Messages
 
TwYM
TwYMTwYM
TwYM
 

プログラマのためのテスト2

  • 1. プログラマのためのテスト Kuniaki IGARASHI http://igarashikuniaki.net/tdiary/ igarashikuniaki@gmail.com 2007.4.24
  • 3. UnitTest ユニットテスト メソッド単位でコードを検証するテストコードを書き、 戻り値、副作用が妥当であることを確認するテスト UnitTest(C++/CPPUnit)のCode例 // テスト対象メソッド addition(int arg1, int arg2) // 引数の和を返し、メンバ変数m_lastResultに結果を格納するメソッド int result = addition(2,3); // テスト対象のメソッドを実行して CPPUNIT_ASSERT_EQUAL((int) 5, result); // 結果を確認 CPPUNIT_ASSERT_EQUAL((int) 5, m_lastResult); // 結果を確認 Green テスト成功 Red テスト失敗
  • 4. UnitTestの基本方針 • 確実にNGになるケースをまず書こう テストが実行されていることを確かめましょう Red テスト失敗 例:CPPUNIT_FAIL(quot;messagequot;); /// 必ず失敗し、messageを表示するテスト テストできたらCPPUNIT_FAILを外してGreenにします。 Green テスト成功 • テストを書こう 新規実装であれば仕様をコードに落としながら。 既存実装の修正 1. 既存のコードに合致するテストを書く。 Green テスト成功 2. 修正後の仕様を満たすテストを書く。 Red テスト失敗 • 本番コードを書こう テストがGreenになるように本番コードを書いていきます。 Green テスト成功 テストで仕様抜け、テストコードパス抜けを見つけたらテストを書き足します。 • リファクタリング 必要があれば、テストを成功のまま保ちつつリファクタリングを行います。 Green テスト成功
  • 5. テスト基本形 // テスト対象メソッド Calc::addition(int arg1, int arg2) // 引数の和を返し、メンバ変数m_lastResultに結果を格納するメソッド void CalcUnitTest::test_Addition(void) { Calc calc; // テスト対象クラスを作成 int result = calc.addition(2,3); // テスト対象のメソッドを実行して CPPUNIT_ASSERT_EQUAL((int) 5, result); // 結果を確認 CPPUNIT_ASSERT_EQUAL((int) 5, calc.m_lastResult); // 結果を確認 // ↑calc.m_lastResultにテストクラスからアクセスできるように、 // テスト対象クラスのヘッダファイルで以下のようにfriend指定しておきます。 // friend class FrameBuilderUnitTest; } CPPUNIT_ASSERT_EQUAL (x,y) → x==y の場合にOK。 CPPUNIT_ASSERT_EQUAL((int) 期待値, テスト対象値); 通常、期待値を第1引数に、テスト対象値を第2引数に渡します。 型をCPPUnitに伝えるために、明示的にキャストする必要があります。
  • 6. 例外送出のテスト /// テスト対象 Yarinage::Set(Yari yari) /// 槍をセットされると槍型の例外を投げるメソッド。 void YarinageUnitTest::test_Set(void) { CPPUNIT_ASSERT_THROW(実行,送出予期型); Yarinage yarinage; // テスト対象クラスを作成 CPPUNIT_ASSERT_NO_THROW(); Yari yari; // テストに使うクラスを作成 CPPUNIT_ASSERT_THROW(yarinage.Set(yari), Yari); // 飛ばないことを確認するには // CPPUNIT_ASSERT_NO_THROW(); void YarinageUnitTest::test_Set(void) } { Yarinage yarinage; Yari yari; try{ yarinage.Set(yari); // NGパターン CPPUNIT_FAIL(quot;何も飛んできませんでしたよ。quot;); } catch (Yari e) { // OKパターン 槍が投げられました。 または、特別なことをせず } catch (...){ 意図しないコードパスにCPPUNIT_FAILを // NGパターン CPPUNIT_FAIL(quot;槍じゃないものが飛んできましたよ。quot;); 入れてNGにすれば良い。 } }
  • 7. 全パス・全状態の網羅 直交表や全ペアといったテスト技法が助けになります →参考文献:「はじめて学ぶソフトウェアのテスト技法」 直行表
  • 8. Mock(Stub) 制御可能なニセモノ下位モジュール ホンモノコード テストコード テスト対象モジュール Calc::Calc(){ // コンストラクタ #ifndef UNITTEST m_paiCalc = new PaiCalc(); // ホンモノ #else m_paiCalc = new PaiCalcMock(); // ニセモノ #endif } PaiCalc PaiCalcMock GetResult (1000000) GetResult (1000000) 5分計算して結果を返す てきとーな値ですぐに結果を返す 返す値はテストコードから制御可能 C++ヘッダファイルからMockを生成するMockMaker作りました。 (C++ヘッダのパースがまだまだ全然ダメですが) http://igarashikuniaki.net/data/MockMaker/MockMaker.zip
  • 9. メソッド呼び出しの確認 テスト対象モジュール テストコード Foo::DoCalcPai() g_is_ PaiCalcMock_GetResult _Invoked = false; 内で foo.DoCalcPai(); // テスト実行 PaiCalc::GetResult() CPPUNIT_ASSERT_EQUAL((bool)true, が呼び出されるか g_is_ PaiCalcMock_GetResult _Invoked); 確認するテスト Foo(テスト対象モジュール) Foo::DoCalcPai() { m_paiCalc->GetResult (1000000); // Test時はMockが呼び出される } PaiCalcMock PaiCalcMock::GetResult (1000000){ g_is_ PaiCalcMock_GetResult _Invoked = true テスタで導通チェックを // メソッド内でグローバル変数に値代入 するようなものです。 }
  • 10. UnitTest実装時のCheckList • 全てのパスを通過 • 全ての環境(変数やメモリ状態)の組み合わせ • 全ての異常系 • 全ての状態遷移 • 例外送出 • NULLチェック • 境界値 • 変換 - 逆変換 - diff • Initializeが呼ばれる前に全メソッドを呼ぶテスト • Finalizeが呼ばれた後に全メソッドを呼ぶテスト • Initializeを呼んだ後にInitializeを呼ぶテスト
  • 11. Test First Development Test Driven Development 本番コードを書く前にユニットテストコードを書く → テストコードに駆動される開発 テストコードで境界値や全てのケースを網羅しようという意識になる 仕様が固まっていない部分は固めようとする テスト容易な設計を考慮するようになる テストを満たすように、突貫コードを書く 仕様破綻がないかチェックする エラーケースをケアするコードを書く リファクタリング
  • 12. テスト容易な設計とはなんだ? Mockオブジェクトをどうやって置き換えるか? • #ifdef (前述) Foo::Foo(){ // コンストラクタ #ifndef UNITTEST m_bar = new Real(); // ホンモノ #else m_bar = new Mock(); // ニセモノ #endif } • DI(Dependency Injection:依存性の注入) (例: GoFのストラテジーdesign patternを利用)
  • 13. Dependency Injectionで Real/Mock切りかえ RealBar SetFoo(InterfaceFoo* foo){ InterfaceFoo m_foo = foo; Calc() 純粋仮想関数 } m_foo->Calc(); RealFoo MockFoo Calc()を実装 Calc()を実装 // 本番時はRealFoo // テスト時はMockFoo 本番コード テストコード RealMain(){ TestMain(){ RealBar bar; RealBar bar; bar.SetFoo(new RealFoo()); bar.SetFoo(new McokFoo()); ... ... } } 本番コードにテストコードが含まれないスッキリした設計・実装!
  • 15. サンプルコード ==== DI と Mock Object を利用した UnitTest Pattern ==== ■テストコード // 実際は Mockpp を利用して、より柔軟にテスト条件に応じた値を返す Mock を作る ■ 本番コード class MockFoo : public IFoo { Bar class は Foo class の演算結果を利用して処理をします。 int m_calc_value; class IFoo : public IFoo { void SetCalcFoo(int value) { int CalcFoo(); m_calc_value = value; } } Mockpp を利用することで尐し楽をして Mock class を作成できる ようになります。 class RealFoo { int CalcFoo() { int CalcFoo() { return m_calc_value; このようにして Mock Class を一度作っておけば、テスト本体だけで return 3; } テスト条件を記述できるようになり、テスト時も本番時もまったく } } 同じオブジェクトを利用できるようになります。 } # リビルド不要、本番とテストでコードの食い違いが起こりえない void test_main() class RealBar { また、必ず依存関係の間にインタフェースが挟まるので、 { MockFoo foo; それぞれのクラスを別々に開発することも容易になります。 IFoo *m_foo; RealBar bar; # 依存先の実体がなくてもコンパイルできるし、Mock を入れて # 逐次テストができる void SetFoo(IFoo* foo) { bar.SetFoo(&foo); m_foo = foo; つまり、DI + MockObject で UnitTest というのは、このような } foo.SetCalcValue(1); メリットを得るために、本番コードのアーキテクチャを変えるという ASSERT(bar.CalcBar() == 2); extreme なテスト手法です。 // CalcFoo() の結果を 2倍する int CalcBar() { foo.SetCalcValue(2); return 2 * m_foo->CalcFoo(); ASSERT(bar.CalcBar() == 4); } } } void main() { RealFoo foo; RealBar bar; // Setter Injection. bar.SetFoo(&foo); } printf(quot;CalcBar = %d¥nquot;, bar.CalcBar()); (c)ひぐまさん 多謝!!
  • 16. Javaの世界は一歩先を行く • Seasar2 (DIコンテナFrameWork) コンテナの中身を外部ファイルで自由に操作 .xml コード 使用したいコンテナ(クラ aのインスタンスは○○ ス)の名前だけしっていれ bのインスタンスは△△ ばいい。 cのインスタンスは□□ そのインスタンスが何か ... は意識しなくて良い。 DIコンテナ 本番・テストごとにxmlを切り替えてReal/Mockを指定する。 Real.xml Mock.xml TestA.xml aのインスタンスはRealA aのインスタンスはMockA aのインスタンスはRealA bのインスタンスはRealB bのインスタンスはMockB bのインスタンスはMockB cのインスタンスはRealC cのインスタンスはMockC cのインスタンスはMockC
  • 17. UnitTestPatterns ■The Simple-Test Pattern(シンプルテストパターン) どのようなUnitTestを書け ■The Code-Path Pattern(コードパスパターン) ■The Parameter-Range Pattern(パラメータレンジパターン) ばいいのかを形式化し、テ ■Data Driven Test Patterns(データ駆動テストパターン) ■The Simple-Test-Data Pattern(シンプルテストデータパターン) ストファーストをプログラマ ■The Data-Transformation-Test Pattern(データ変換テストパターン) ■Data Transaction Patterns (データトランザクションパターン) の習慣にすることを目指す ■The Simple-Data-I/O Pattern(シンプルデータI/Oパターン) ■The Constraint-Data Pattern(制約データパターン) ■The Rollback Pattern(ロールバックパターン) webページ ■Collection Management Patterns(コレクション管理パターン) ■The Collection-Order Pattern(コレクションオーダリングパターン) http://www.marcclifton.com/tabid/87/Default.aspx (英語) ■The Enumeration Pattern(列挙パターン) ■The Collection-Constraint Pattern(コレクション制約パターン) http://igarashikuniaki.net/fswiki/wiki.cgi?page=UnitTestPatterns ■The Collection-Indexing Pattern(コレクションインデクシングパターン) (日本語翻訳途中) ■Performance Patterns(パフォーマンスパターン) ■The Performance-Test Pattern(パフォーマンステストパターン) ■Process Patterns(プロセスパターン) ■The Process-Sequence Pattern(プロセスシーケンスパターン) ■The Process-State Pattern(プロセス状態パターン) ■The Process-Rule Pattern(プロセスルールパターン) ■Simulation Patterns(シミュレーションパターン) ■Mock-Object Pattern(モックオブジェクトパターン) ■The Service-Simulation Pattern(サービスシミュレーションパターン) ■The Bit-Error-Simulation Pattern(ビットエラーシミュレーションパターン) テストしやすい設計をするために、テストファースト。 テストを設計前に書けば、テストまで考慮した設計が可能。
  • 19. テストを育てる テストが書かれている範囲はコントロール 下にあるということ。 テストを追加していくということは、 テストにより動作保証される範囲を増やし、 リスク把握できる部分を広げていくこと。 ※NGでひっかかった回数 OK を記録するような仕組みが あると面白いね。 OK OK 現在、テストがカバーしているコードがどの辺なのか バグの場所が分かる 見える化してくれるツールがあるといいですね。 ドラゴンレーダーが欲しいです。
  • 20. UnitTestツール xUnit •JUnit (JAVA) •NUnit (C#) •CppUnit (C++) どの環境でもユニットテストツールは大抵あります。 CppUnit動作環境 VC++, g++(GCC)など。
  • 21. UnitTestのまとめ • テストにより駆動される開発 – 気づけなかったものに気づく – 発想を広げる • テスト容易性まで考慮した設計 – モジュール化重要 • テストを用いたリスク解析 テスト△はOKなので、△の範囲は問題無し テスト□がNGだったので、□の範囲内に問題有り
  • 22. 参考文献 • はじめて学ぶソフトウェアのテスト技法 著:リー・コープランド ISBN : 4-8222-8251-1 これ1冊でプログラマレベルではテストマスター • CPPUnitによる実践テスト技法 http://www.mikamama.com/CppUnitBook/draft/index.html (draft) 著:大月美佳 ISBN:4-7980-0571-1 ユニットテストの入門書。リファレンスとしても便利。 • 知識ゼロから学ぶソフトウェアテスト 著:高橋寿一 ISBN:4-7981-0709-3 SONYのテストエンジニアさんの著書。 実際の経験に基づくノウハウが多数。 • WEB+DB PRESS vol.35 著:和田卓人 ISBN:4-7741-2931-3 Java DIコンテナSeasar2を使ったユニットテストの記事など入門記事。
  • 24. テストのゴールはいつだ? テストにかかった費用 + サポートにかかる費用 + メンテナンスにかかる費用 が最小値になるとき