システムの品質とUTについて

この投稿は、社内の勉強会向けのパワポをMarkdown形式で起こしたものです。

## はじめに - この勉強会の目的

この勉強会の最終目標は以下のとおりです。

>プロジェクト内でUTを普及させ、UTレベルのバグによる障害を抑止したい！

上記の最終目標を達成するために、こんなことをやっていく予定です。

1. そもそもシステムの品質とは何なのかを明確化する
1. システムの品質=金の問題であることを明確化する
1. UTの位置づけを明確化する
1. UTの技本的な技法を解説する
1. UTを開発プロセスに組み込む
    * 最終的にUTの提出を義務化したいなぁ
   
この勉強会では上記のステップのうち、上から3つまでを扱います。
具体的なテストの技法以前にのフィロソフィーのみを解説します。

## テストは何のためにやるの?

テストはなんとなく大事な気がしますが、そもそもなんでテストをやるのでしょうか。
エンジニアとしてのプライド？ 他人に迷惑をかけたくないから？ etc...
そうじゃないよ、ということをこれから説明します。

### テストの目的

よく勘違いされますが、

テストの目的は*「バグがないことを証明するため」*ではありません。
テストの目的は*「バグがないことを証明するため」*ではありません。
テストの目的は*「バグがないことを証明するため」*ではありません。

非常に大事なことなので三回書きました。
少し考えればわかりますが、よほどシンプルなプログラムでない限り、
取りうるパターンや状態は無限大といってよいほどあります。
ちょっとしたWebサービスでも機能の正常、異常以外に、以下のような外的要因に左右されます。

* ブラウザの種類 => 特定のブラウザだけうまく動かない!
* ネットワークの状態 => 謎のネットワーク遅延が！
* 連携先サービスの状態 => 明らかに仕様違反の結果を返す！
etc...

上記のような様々な状態、事前条件をすべて網羅するのは現実的には無理でしょう。
では、 何のためにテストをやるのでしょうか。

### テスターのメンタル・ライフ ~進化するテスター!~

とあるおじさんが提唱した、「テスターのメンタル・ライフの5つのフェーズ
」というものがあります。

|     フェーズ名   |      概要     |
|:----------------------------|:-------------------------------------------------------------------------------------|
| フェーズ0 | テスト=デバッグ(ステップ・デバッガで動きを確認しました=テストしました！) |
| フェーズ1 | 対象システムが*期待通り動くことを証明する*ための活動 |
| フェーズ2 | 対象システムが特定の条件で動かないことを証明する*ための活動 |
| フェーズ3 | *テストは何も証明しない*が、システムが受け入られるレベルの価値を発揮できる状態かどうかをモニタリングし、*不十分な品質のままリリースしてしまうリスクを減らすための活動* |
| フェーズ4 |*テストは活動ではない。* そもそもテストをやる以前にバグを潰すマインド・セットを養成するための*精神修行である。* |

フェーズ4はちょっと特殊で精神論っぽいですが、フェーズ3の「リスク管理の一環としての*システムの品質のモニタリング活動*である」というのは、システム開発で重要なものは何なのかを示唆しています。
次は、今回挙がった*「システムの品質」*とはそもそも何なのかを深堀します。

## システムの品質とは?

### 深くて難しい「品質」 ~ かっこいいGUIだけじゃないんですよ、品質は! ~

そもそもシステムの品質とは何なのでしょうか?
かっこいいGUI? レスポンシブ・デザイン? 機能がたくさんあること?
人によって思い浮かぶことは様々だと思いますが、IEEEが定義するシステムの品質を要約すると、

「システムがステーク・ホルダの要望、期待を満たしているかどうか」

ということになります。

小規模なWebサービスでも、システムのステーク・ホルダはたくさんいます。

* プロダクト・オーナー
* PM
* 運用
* 営業
* クライアント/ユーザー
* 開発者
etc...

ステーク・ホルダが、それぞれ独自のシステムに対する要望や期待があり、
相反する要望を満たさなければならないこともあるでしょう。

また、詳細は非常に長くなるのでIEEEの定義する品質モデルの解説は割愛しますが、前述のステーク・ホルダがシステムに何を期待しているのかを考えれば、
システムとしてどんな品質特性を定義、維持管理することが必要か想像がつくと思います。以下はその一例です。

* 機能性、ユーザビリティ、信頼性、性能、運用性 etc...

例えばB to Bで売り込みたいのに、データ不整合やメールの誤配信を繰り返していたら、
損失補てんや機会損失のためにプロダクト・オーナーの想定する売上、利益を達成するのは難しいだろうし、
運用性が劣悪だと運用がボトルネックになって、システムをスケールするのが難しいでしょう。
ほかにも、レスポンスが遅いと、そもそもユーザーが使ってくれないかもしれません。

プログラミングやデザイン・パターンなどのツールの話ばかりが注目されがちですが、
システム開発の目的は

「ステーク・ホルダの要望、期待を満たすこと」

なので、ツールの話をする前に、そもそも何のために仕事をしているのか、考えるべきですね。
次はシステムの品質に対するアプローチについて見ていきたいと思います。

### Gravin‘s Quality Approaches ~ アプローチは一つじゃない! ~

昔のおじさんが品質に対するアプローチとして提唱した

「Gravin‘s Quality Approaches」

という考え方があります。上にいくほど定性的で計測が難しく、下に行くほど定量的で計測が容易です。

|     アプローチ名   |      概要     |
|:----------------------------|:-------------------------------------------------------------------------------------|
| Transcendent approach | 色んな人の客観的に評価できないフィーリング。(なんか凄く良い気がする、こんな感じの!) |
| User-based approach | ユーザーのニーズを最も良く満足させたものが、最も品質が高い。品質≠満足度なので、品質が低くても問題ないこともある。|
| Value-based approach | 製品のコストと、満たせている要求を比較する。例：信頼性が低くとも、コストが1/5ならトータルのValueは信頼性が高く、コストが5倍の製品に勝る
| Product-based approach | 製品が持つべき品質特性と、現状の品質との差分。ソフトウェアだと計測が結構難しい（が、できないわけではない) |
|Manufacturing approach|システムの製造工程(=実装)に着目する。例:  バグ密度、バグ収束曲線|

もちろん、どのアプローチが有効なのか、どの程度の品質が必要なのかは、
開発するシステムの要件によって様々だと思いますが、
*どのアプローチも非常に重要で無視できない*ことがわかります。

次は、各アプローチとシステムのライフサイクルの関連について解説します。

### Webサービスのライフサイクルと品質に対するアプローチの関連付け

同一システムであったとしても、システムの現在のライフサイクルによって、
重視すべきアプローチは変わってきます。Webサービスの立ち上げを例に解説します。

|     Webサービスのライフサイクル   |      重視すべきアプローチ     |
|:----------------------------|:-------------------------------------------------------------------------------------|
| サービスの立ち上げ時 |User-based, Value-based approachが最も大事！そもそも需要があるのか?|
|サービスがある程度立ち上がった後|Product-based approachに力点をシフト。顧客がつき始めているので、セキュリティ、性能などの品質特性が重要になります。|
|サービスが立ち上がって安定期に入った後|Manufacturing-based approachも重要に。大量の顧客がいるし、重大なバグ＝サービスが即死の可能性があるので、製造業的な品質管理が重要に!|

上記は品質特性に対するアプローチの考え方をWebサービスの開発に適用した一例ではありますが、
一度立ち上がったサービスは、*品質を管理し、バグを出さないことがとても重要*であることがわかります。
次はバグとコストの関係について見ていきます。

## バグが見つかった時のコスト ~ バグのコストはいくら? ~

システムのバグは、見つかるタイミングによって大雑把に2つに分類できます。

* Internal Failure(内部故障)
    * システムの開発組織内部で見つかったバグ。もちろん、修正にはコストがかかるが、下記の「External Failure(外部故障)」と比較して、相対的に影響が少ない。
* External Failure(外部故障)
    * システムのリリース後に、開発組織の外部で見つかったバグ。
    * バグの修正、再テストなどのコストに加えて、以下のコストもかかります！
        * ***再リリースのコスト***
        * ***顧客への説明のコスト***
        * ***サポート、ヘルプデスクのコスト***
        * ***ビジネス機会損失のコスト***
        * ***会社の評判が悪くなることによるコスト***
        
もちろん、完璧な品質を作りこむのは無理だし、バグの内容にもよりますが、
要するに一度リリースしてしまった後にバグが発覚するコストは非常に高くつくのです。
下手をすると、ちょっとしたつまらないバグでも、***Webサービスが消し飛びかねません。***

複雑な条件による結合試験や、負荷試験の実施は難しいかもしれませんが、
***もっとも基本的なテストであるUT(Unit Test)***で防げるバグはUTで極力防いでいきたいですよね。
次は、UTとは何なのかについてみていきます。

## Unit Testとは何か?

### UTの位置づけ ~ みんな大好きVモデル! ~

無断で画像を引用できないので、Vモデルの詳細は以下のリンク先を参照してください。

[Vモデル - Wikipedia](http://ja.wikipedia.org/wiki/V%E3%83%A2%E3%83%87%E3%83%AB)

要するにUTとは、***詳細設計の成果物=クラス、関数単位の振る舞いを確認するためのテスト***になります。

プログラマにとっては一番身近で、結合テストやシステムテスト、受け入れテストと比べて相対的に低コストで実施できますし、
粒度が細かく、***UTでの品質を担保できていないとより上流のテストでの品質の確保も難しい***でしょう。
普通は受け入れテストや結合テストでUT観点のテストは実施しないし、
本番でUT観点で防げそうなバグがでようものなら、責任者の首が危ないかもしれませんね！

ただ、UTもとにかくやれば良いというものではないので、次は「良いUTの条件」を見ていきます。

### 良いUTの条件 ~ UTを飾りにしないために... ~

良いUTは以下の特性を持つ必要があります。

* 自動化され、繰り返し実行可能性であること
* 実装が容易であること
* 一度書いたら、将来にわたって維持管理できること
* 誰でも簡単に実行できること
* 拘束に実行できること

要するに手動でその都度、ぽちぽち画面を叩いて実行するようなものではなく、
専用の自動実行ツールを活用したほうが効率が良いということです。
また、「実装が容易であること」というのは、システムの設計自体が
初めからUTの容易性が考慮されている必要があります。
(ここではxUnitの話はしません)

### UTの例

UTの一例として、以下のシンプルな関数を考えてみます。

```php
/**
  * 引数の数値に1を足してインクリメントした数値を返します。
  * @return number
  * @throws Exception 引数が数値型以外の時
  */
function increment($num) {
 …	
}
```
上記の関数をテストする場合、少なくとも以下のケースは考える必要があります。

* $num	の代表値 => Number型の範囲のうちの一つ 例: 「1」を渡して「2」を返すか?
* $numがNumberの最大値の場合  => そもそもどう振る舞うのが正しい?
* $numが数値以外の場合 => 例外を投げるべき
* $numがnullの場合 => 例外を投げるべき

こんなシンプルな関数でもこれだけのケースを考えなければならないのですね！
また、上記のケースの洗い出し方から、以下のこともわかります。

* テストはエンジニアリングの知識が必要 => 少なくともフィーリングだけではだめですね。
* テストは関数の仕様と密接に関連する => 関数の仕様が明確でないとテストできないですね。

## まとめ

* システムの品質の計測、維持管理は極めて重要です!
* システムの品質が悪いとサービスが潰れちゃうかもよ!
* UTで防げるバグは極力防ごうよ!

## 練習問題

* Q1. テストは何故必要なのですか？ あなたの言葉で説明しなさい。
* Q2. UTとは何ですか？ UTの概要、必要性をあなたの言葉で説明しなさい。
* Q3. あなたの過去の開発経験の中で、UTがあれば防げたであろうバグと、作成すべきUTの内容をあげなさい。
* Q4. 先週あなたが書いたプログラムはUTがきちんとできていたかを説明しなさい。
* Q5. 先週あなたが書いたプログラムはUTを作成することができるかどうかを説明しなさい。
* Q6. あなたはUTを実施するだけのエンジニアリングの知識が備わっているかを述べなさい。もし、備わっていないとしたら、何をすべきかのアクション・プランを説明しなさい。

## 参考文献

<dl>
<dt>Pragmatic Software Testing: Becoming an Effective and Efficient Test Professional
<dd>網羅的でとてもためになります。練習問題がすごい!
<dd><a href="http://www.amazon.co.jp/gp/product/B004WG3UAA/ref=as_li_qf_sp_asin_il?ie=UTF8&camp=247&creative=1211&creativeASIN=B004WG3UAA&linkCode=as2&tag=sisimai1987-22"><img border="0" src="http://ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=B004WG3UAA&Format=_SL160_&ID=AsinImage&MarketPlace=JP&ServiceVersion=20070822&WS=1&tag=sisimai1987-22" ></a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=sisimai1987-22&l=as2&o=9&a=B004WG3UAA" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
<dt>The Art of Unit Testing: With Examples in C#
<dd>記述が非常に簡潔でわかりやすいのでお勧め。
<dd><a href="http://www.amazon.co.jp/gp/product/1617290890/ref=as_li_qf_sp_asin_il?ie=UTF8&camp=247&creative=1211&creativeASIN=1617290890&linkCode=as2&tag=sisimai1987-22"><img border="0" src="http://ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=1617290890&Format=_SL160_&ID=AsinImage&MarketPlace=JP&ServiceVersion=20070822&WS=1&tag=sisimai1987-22" ></a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=sisimai1987-22&l=as2&o=9&a=1617290890" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />
<dt>Software Product Quality Control
<dd>ソフトウェアの品質について
とても考えさせられます。品質要件を定義してPDCAをちゃんと回したい...
<dd><a href="http://www.amazon.co.jp/gp/product/B00E5U5HB8/ref=as_li_qf_sp_asin_il?ie=UTF8&camp=247&creative=1211&creativeASIN=B00E5U5HB8&linkCode=as2&tag=sisimai1987-22"><img border="0" src="http://ws-fe.amazon-adsystem.com/widgets/q?_encoding=UTF8&ASIN=B00E5U5HB8&Format=_SL160_&ID=AsinImage&MarketPlace=JP&ServiceVersion=20070822&WS=1&tag=sisimai1987-22" ></a><img src="http://ir-jp.amazon-adsystem.com/e/ir?t=sisimai1987-22&l=as2&o=9&a=B00E5U5HB8" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />