FLINTERS Engineer's Blog

FLINTERSのエンジニアによる技術ブログ

エンジニアとしてもっておきたいロジカルシンキング入門(テスト編)

0.前座

皆様、お久しぶりです。始めての方ははじめまして。エンジニアの原田です。

ついに、書く書く詐欺ではなくなりました。ロジカルシンキング第二弾です。

はてなブログに移ったので前の記事はどこなのーがわかりにくいので一応、リンク貼っておきます。 エンジニアとしてもっておきたいロジカルシンキング入門 - Septeni EngineerBlog

もし、気になったなーとか思っていただけると幸いです。

今回は、ロジカルシンキングの実践落とし込みの第一弾として、テストでロジカルシンキングを使ってみたいと思います。

1.MECE

よく語られるMECEという言葉の例をあげてみます。 もし、知ってるよーということであれば、この章、飛ばしてしまってもいいです。

1-1. MECEとは?

Mutually Exclusive、Collecticely Exhaustiveの略です。 日本語にすると「漏れ無くダブりなく」という意味になります。

つまり、MECEとは漏れもダブりもない状態のことを表します。

1-2. 実例(お題)

まぁ、ググればいくらでもでてくるのですが、一旦はコードから離れた実例を出します。

例えば、とあるWebページのユーザーをMECEで分割してみましょう。

1-3. 実例(解答例)

解答例です。考えることができたでしょうか?

例)

男性ユーザー / 女性ユーザー

成人ユーザー / 未成年ユーザー

スマートフォンユーザー / スマートフォン以外のユーザー

悪い例)

男性ユーザー / スマートフォンユーザー / 女性ユーザー (ダブっている。)

スマートフォンユーザー / ガラケユーザー (漏れあり、PCユーザー、タブレットユーザー)

このように、漏れもダブりもない分類をすることがMECEの基礎となります。

2.MECEでテストを書こう!

2-1. なぜ、MECEをテストに?

説明するまでもないかも知れませんが、MECEによるテスト分析はかなり有効です。

なぜ有効かというと、まずテストケースが漏れがないからです。 (理屈上では、MECEが完璧な場合、全てのテストケースが通ればバグがないがなりたちます。)

そして、テストとは品質をあげるコストフェーズになりますので、もちろん効率的に行いたいものです。 なのでダブりなくはとても有効な一手となるわけです。

2-2. まずは図と簡単なコードによる概論から

例えば、二分木でA、Bの達する関数があれば、それに対するテストはA、Bが正しく導ける2ケーステストすれば良いということになります。

図1 f:id:y_harada:20150725224021p:plain

コードで書くとこんな感じになります。

object SampleTestClass{
    def targetCalculation(c:Int) = if(c > 3) "a" else "b"
}


// テストクラス実装
class SampleTestClass extends Specification {

   "targetCalculation" should {
       "3より大の場合は、a" in {
           SampleTestClass.targetCalculation(5) must beEqualTo("a")
       }
       "3以下の場合は、b" in {
           SampleTestClass.targetCalculation(2) must beEqualTo("b")
       }
   }

}

恐らく、これぐらいの関数では、MECEによるテストケース分割をしくじる方は少ないかと思います。

2-3. ちょっとした複雑なロジック例

実際には、さらに複雑になりますが、最小のこのifのみの関数と同じように考えてどれくらいのケースを作ればよいのかを考えてやると解に近づきます。

例えば

def getOddChars(targeString:String) = targetString.zipWithIndex.foldLeft("")((x, y) => if (y._2 % 2 == 1) {x + y._1} else {x})

foldLeftやら、zipWithIndexやらで目が曇りがちですが実際は3ケースで十分です。(僕は安心のために4ケース書きますが…)

・空文字のケース ・1文字のケース(奇数のチェック) ・2文字のケース(偶数のチェック)

2-4. よくあるMECE失敗例

MECEで考えるとMECEな状態でないのは2つのケースしかないのですが(笑 漏れが発生している例と無駄が発生している例を簡単にあげておきます。

漏れが発生している例)

object SampleTestClass{
    def targetCalculation(c:Int) = 100 / c
}


// テストクラス実装
class SampleTestClass extends Specification {

   "targetCalculation" should {
       "割り算できる" in {
           SampleTestClass.targetCalculation(2) must beEqualTo(50)
       }
   }

}

ありがちですが、0割の考慮がテストケースから漏れている例です。 そもそも、上位層から0で引き渡されるからと言ってもこの関数からは知る余地もない話なので、 0割のテストは書いておかなければ、ならないと考えております。

無駄が発生している例) 実例出すのが難しいので、意識ポイントのみですが…

・自分の書いたコードではなく、Frameworkのテストをしてしまっている。 ・モデルで担保したFanctionをControllerで再度テストしている。

3. まとめ

ざっと書きましたが、MECEは複雑な要件になればなるほど難しいです。

で、

最後になにが言いたいかというと。

途中で出したA-Bのif文のみの関数を思い出してください。

あれだけ小さければMECE簡単じゃろ?そう関数をシンプルにすればMECEは簡単になるのです。 複雑な要件も、単純なif文の塊なので小さくしてやれば、網羅率100%も全然夢ではないと考えています。