なみひらブログ

学んだことを日々記録する。~ since 2012/06/24 ~

不具合に対するテストの書き方

本文は、以下の書籍を参考にしました。

継続的インテグレーション入門 開発プロセスを自動化する47の作法

継続的インテグレーション入門 開発プロセスを自動化する47の作法

 

想定外のアプリケーションの動きによって不具合が見つかった場合、

その対応として第一歩として、この不具合を明らかにするためのテストケースを書きます。

 

例えば、下記のようなメソッドを実装して実行してみると、

public int findWord(String str){

     int value = databese.get(str);

     return value;

}

例外が発生してしまうという不具合が発生しました。

現象としてはFindException(データベースから文字列strに一致するものが見つけられません。。。)

が発生しているようです。

 

この対応のために、この不具合を再現するテストコードを書きます。

public void testFindWord(){

     String str = "test";

     try{

          int value = databese.get(str);

     } catch (FindException e) {

          TestCase.fail("Don't find word" + str); //例外が発生したらテスト失敗

     }

}

 

この時点ではこのテストコードを実行すると、もちろん例外が発生しテスト失敗になります。

この後、例外が発生しないようにfindWordを修正し、テストが成功するまで修正を繰り返します。

今回のケースでは、databaseのサイズが0の場合が十分に想定されていなかったようです。

今回はとりあえず0を返すようにしておきます。

public int findWord(String str){

      if (database.size() == 0) {

            return 0;

      }

      int value = databese.get(str);

      return value;

}

 

これでテストを実行してみると見事にテスト成功しました。

修正完了!めでたし、めでたし。。。。。

 

。。。。。と、多くの開発者は行動すると思います。

この手法は、「欠陥駆動開発」といわれる手法で広く行われています。

 

しかし、この手法には以下の問題点があります。

  • このテストコードは単に例外を発生しないことを確かめているにすぎない。。。
  • 修正した後の振る舞い(今回の例ではdatabaseのサイズが0の場合に0を返すという仕様)を確かめていません。。。

Aさん「じゃあ、最初からテストコードで0を返すことを確かめりゃいいじゃん!」

Bさん「けど、最初はどこに不具合があって、どう修正するかわかんないのに、そんなテストコード書けなくない?」

Aさん「確かに(´・ω・`)。。。。。」

 

ってな、感じになります。

 

以下からが、開発者は行うべき行動です。

-----------------------------------------------------

  1. 例外が発生したらテストが「成功」となるテストコードを書く。
  2. そのテストコードを失敗するようにコードを修正する。(=例外が発生しなくなる)
  3. 修正した新しい振る舞いを確認するために、テストコードを修正する。
  4. テスト実行->成功。めでたし、めでたし

 

具体的なコードは以下の通り、

 

1.

public void testFindWord(){

     String str = "test";

     try{

          int value = databese.get(str);

          TestCase.fail("This should throw an Exception"); // 例外が発生しなかったらテスト失敗

     } catch (FindException e) {

          TestCase.assertTrue(e + "OK"); //例外が発生したらテスト成功

     }

}

 

このテストコードを実行すると例外が発生しますが、

テストとしては成功です。これで不具合を再現したことになります。

 

次にコードの修正です。これは前と同じです。

 

2.

public int findWord(String str){

     if (database.size() == 0) {

            return 0;

     }

     int value = databese.get(str);

     return value;

}

 

この修正により例外が発生しなくなり、テストは失敗になります。

次に、修正した新しい振る舞い(0を返す仕様のこと)を確認するために、

テストコードを修正します。

 

3.

public void testFindWord(){

     String str = "test";

     try{

         int value = databese.get(str);

        TestCase.assertNull("should have received back 0", value == 0); // 0ならOK

      } catch (FindException e) {

        TestCase.fali("This shoud not throw an exception"); //例外が発生したらテスト失敗

      }

}

 

4.テスト実行すると、成功となります。

 

このアプローチは、従来の「欠陥駆動開発」と同様に

  • 不具合の修正
  • 不具合の再発の防止(テストコードの資産化)

に加え、

  • 不具合の修正による新しい振る舞いの検証

を行うことできます。