なんと今回は記念すべき 第20回目のテスト勉強会でしたっ!!
ホントに皆さんのおかげです。ありがとうございます!!
∋(。・"・)-†.:*・゚☆祝20回☆゚・*:.†-(・"・。)∈
10月のテーマはTDDBCとのコラボで、TDDBC仙台the3rdのお題をお借りして、テストコードのテストケースを考えてみよう!ということで勉強会を行いました。
テストコードは何も考えないと、漏れが起こったり、ドンドン増えてしまいがちですよね。
今回はテストコードを書いていくときに、どうやったら漏れのないテストケースを導くことができるか考えていきました。
場所はソシラボさんです。ありがとうございます!
まずは簡単にTDDBCの紹介と今回の趣旨の説明をしました。
テストコード書いたことが無い参加者もいるので、まずは簡単な例題から。
【注意!】
今回はほぼブラックボックスのイメージでテストケース書いてもらっていますので、多めのテストケースになります。実際のTDDはここからコードを書いたり、リファクタすることで、最適なテストケースにしていきます。
最初の例題は整数の足し算。
***************************************************
[整数の足し算]
以下のメソッドが正しく動作するために必要なテストケース(aとbの組み合わせ)を導いてください。
整数aとbの二つを与えると、足した値が戻り値として返る。
public int Plus(int a, int b)
***************************************************
テストコード書いたことがない参加者に説明しながら実際に値を書いてもらいました。
参加者の値を確認していると色々な考え方があって面白いですね。
個人ワークを実施したあとグループで確認してもらいました。
TDDで開発していくときのコードはこんな感じになると思います。(言語はC#)
public static int Plus(int a, int b)
{
return a + b;
}
ただし、結果がINTMAXを超えるというような仕様は考えていないコードになっています。
有給の計算プログラムで使用するのであればINTMAXなどには届かないと思いますが、お金関係だと超えてしまいそうですよね。
参加者の中にもINTMIN、INTMAXを考える人とそこはテストしないと割り切っている人が居ました。
皆さんはどう考えますか?
続いて第2問目の例題。
***************************************************
[チケット]
杜王町の美術展の入場料には以下のような割引がある。10のつく日(10日~19日)は10%Off、20のつく日(20日~29日)は20%Offです。
以下のメソッドが正しく動作するために必要なテストケース(dateと割引率組み合わせ)を導いてください。
日にちを与えると、割引率(%)が戻り値として返る。
ただし、1未満または31より大きい数値が入力された場合は0を返す。
public static int GetDiscountRate(int date)
***************************************************
こちらの方が考えやすかったらしく、参加者の皆さんはサクサクと書いていきます。
面白かったのは、コードをイメージして、最初に異常系のテストを書いている参加者が居ました。
自分のコードもそうなっていますので、確認してみてください。
TDDで開発していくときのコードはこんな感じになると思います。(言語はC#)
public static int GetDiscountRate(int date)
{
int discountRate = 0;
if ((date < 1) || (31 < date))
{
return discountRate;
}
if ((10 <= date) || (date <= 19))
{
discountRate = 10;
}
else if ((20 <= date) || (date <= 29))
{
discountRate = 20;
}
return discountRate;
}
自分が考えたテストケースの例はこのようになっています。(青は無効同値を表しています)
さてついにTDDBCの問題に突入です。
課題は今回の勉強会用に問題を減らしています。
オリジナルの問題はこちらを参照ください。
http://devtesting.jp/tddbc/?TDDBC%E4%BB%99%E5%8F%B003%2F%E8%AA%B2%E9%A1%8C
もし自分で挑戦したい人は上記リンクの課題1-1、課題1-2、課題1-3のテストケースを自分でしっかり考えてから、本エントリを読んでください。
かなり手ごわいので面白いはずですよ♪
***************************************************
[TDDBC問題①]
1-1
下端点と上端点を与えて閉区間を生成しよう
閉区間から下端点を取得しよう
以下のメソッドが正しく動作するために必要なテストケース(上端点と下端点の組み合わせ)を導いてください。
public int getLowerEndpoint()
***************************************************
ここでは質問が出ました。
例えば、(3, 3)のような閉区間はいいのか? → OK
例えば、(8, 3)のような逆になった閉区間はいいのか → NG (コンストラクタ作成時にエラーになる)
というのも踏まえて、自分は以下のように同値分割をしてテストケースを生成しました。(青は無効同値を表しています)
自分の場合は過去の開発経験より0の周辺を気にする傾向があるようです。
もちろん人によっては同値分割の仕方が異なりますので、どれが絶対の正解というのはありません。
ただし、以下の3つのパターンは必ず網羅する必要があります。
①下端点 < 上端点
②下端点 = 上端点
③下端店 > 上端点
ここはオブジェクト指向言語に慣れていない人はコンストラクタの取り扱いが難しかったようです。
続きまして、問題②に行きます。
***************************************************
[TDDBC問題②]
1-2
閉区間が指定した整数を含むか (contains) 判定しよう
どんなパターンがあるか図を描いてみてください。
以下のメソッドが正しく動作するために必要なテストケース(上端点と下端点と指定ポイントの組み合わせ)を導いてください。
public bool contains(int point)
***************************************************
参加者の皆さんはこの絵は簡単に書いていたようですね。
これはある固定した範囲に対して、同値分割してから境界値にあたる箇所をテストケースとして導出しています。
さてついに、今回の一番面白い問題③に突入です。
***************************************************
[TDDBC問題③]
1-3
閉区間が別の閉区間と接続しているか (isConnectedTo) 判定しよう
どんなパターンがあるか図を描いてみてください。
可能な人はソースコードも考えつつ、以下のメソッドが正しく動作するために必要なテストケース(二つの閉区間の組み合わせ)を導いてください。
public bool isConnectedTo(ClosedRange cr)
***************************************************
この同値分割ってかなり難しいと思います。
まずは絵を描いてみます。
実はこの絵だけだと致命的に足りないものがあるのですが、何か分かりますか?
通常の説明に出ている同値分割は動くところは一つですが、今回動く箇所が沢山あります。
①2つの閉区間の位置
②1つ目の閉区間の大きさ
③2つ目の閉区間の大きさ
もし①だけを考えてしまうと罠にマンマと嵌りまるのです Σ(゚Д゚;)
実際に漏れなく書いた絵が以下のようになります。
実は大きさの概念が無いと⑧を見逃してしまいます。
でもこれってなかなか気づくことができないのですよね。。。
しかもTDDでコードを書いていくと、1-2のContainsが使えそうと思って使っちゃうと⑧のケースが抜けた場合にバグになっちゃいます。
最後に問題作成者の井上さんにコメントをもらいました。
この問題はビックリするほど良く考えられていて、考えるほどに楽しくなります。
本当にこの問題作ってもらってありがとうございます!!
数学の問題に見えますが、実際のソフトウェアだと会議室予約システムなどはこのクラスがそっくり使えるはずですよね。重なっていたら予約できないとかですね。
というところで勉強会を終わろうとしたところ、InteliJ伝道師の今井さんから、このテスト勉強会の意義としてはロジカルにこの1-3のパターンを出すことにあるんじゃないかという宿題をいただきました。
次のエントリでその宿題に回答したいと思います。
確かに次に同じような問題があったときにどうやって気づくかというのは重要ですね!!
今月もありがとうございました!!
参考)
今井さんのブログ
http://d.hatena.ne.jp/masanobuimai/20131018/1382108315
2013/11/20 宿題のエントリを追記しました!