単体試験をする時に考えていたこと

この記事は[ソフトウェアテスト Advent Calendar 2017](https://qiita.com/advent-calendar/2017/softwaretesting)の16日目の記事です。
昨日の15日目は、@Andysumiさんの「[テスト管理ツールをいろいろ試してみた](http://andysumi.hatenablog.jp/entry/test-management-tools)」です。
明日の17日目は、@nakanaoさんの「[私がテスト計画を書く理由](http://nakanao.hatenablog.com/entry/2017/12/17/161117)」です。
# まえおき
初Qiita＆Advent Calendarです。
私は、いわゆるSIerのウォーターフォール開発の下流工程を担当していました。
上流エンジニアの設計書を元に詳細設計、コーディング、単体試験をするお仕事です。

最近の私は、『動作確認』というなんだかよくわからない、そもそもテストなのか？と思うようなことしかしていなくて、テストの感覚がぬるくなった自覚があります。
そこで、昔を思い出すために、
昔の私が単体試験をする時、考えていたことをつらつらと書きだして行きます。
# １．正しいことを証明できるか
## 1〜100の入力値を全部試験する？
1〜100までの入力を受け付ける画面があるとして、
その場合、「入力値に何を指定した試験をするか？」というお話。

私は、1〜100までの値を全部入れて、100回試験することはありません。
　
だいたい、境界値の-1、0、1、2、、、99、100、101を辺りと、
その他の数字を何個か、10、72辺りに入れてみよう、と私は考えます。

ただ、感覚では大丈夫だとは思うけど、
「それって試験したことになるの？58の時だけバグっていたらどうするの？」　
という疑問に答えられず、もやもやしていました。

そんな時に「プログラマのための数学」に出会いました。
## プログラマのための数学
「プログラマのための数学」は、結城浩先生の本で、プログラマと関係する数学ネタの本です。
~~少し前に、第二版も出ました。~~
もうすぐ、第二版も出ます。
https://www.amazon.co.jp/dp/4797395451

本の詳しい紹介は省略しますが、
本の中で数学的帰納法について書れていました。
## 数学的帰納法
数学的帰納法について、詳しくは[こちら](https://ja.wikipedia.org/wiki/数学的帰納法)

高校の時に数学で習いました。
ものすごく簡単にいうと、
　1、2、3、、、N、N+1 と1ずつ増えていく値について、
本当にそうなるよね？ということを、数学的に証明したい時に使える考え方です。

この本の「数学的帰納法」ついて書かれている章を読んで、
高校時代に学んだ数学ともやもやした感覚が結びついて、
それが数学的に証明できる！とわかって、もやもやが晴れました。

また、試験項目を考えている時の自分の感覚は、
数学の証明問題を解いている感覚に近い部分があることに気づき、共感がありました。

「プログラマのための数学」はそれ以外にもいろいろと面白くてオススメです。
# ２．組み合わせを考えるのお供
組み合わせの試験を考える時に、真理値表とベン図をよく使っていました。
学生時代にデジタル回路設計の勉強をしていたことがあり、
それがきっかけで使っていました。
## 真理値表

真理値表について、詳しくは[こちら](https://ja.wikipedia.org/wiki/真理値表)

真理値表は、こういう表です。

|入力A|入力B|出力C|
|:----|:----|:----|
|false|false|false|
|false|true|true|
|true|false|true|
|true|true|false|

マトリクスや組み合わせ表、といった表現の方が伝わるかもしれません。
「こういう入力だと、こういう出力がある」
という組み合わせを表にしたものです。

ただし、組み合わせは、入力が増えると、指数的に倍に倍に増えて行きます。
そこで、増えすぎる組み合わせを、
試験ができる現実的な数にどう減らすかを考えることになります。

|入力A|入力B|入力C|出力D|
|:----|:----|:----|:----|
|false|false|false|false|
|false|false|true |false|
|false|true |false|true |
|false|true |true |true |
|true |false|false|false|
|true |false|true |false|
|true |true |false|true |
|true |true |true |true |

例えば、この表の組み合わせを減らすことができるでしょうか？

表をよく見ると、
出力Cの値が、入力Aと入力Bの値に関係なく、入力Aの値が決まることがわかります。
これで、入力Bを条件から減らすことができます。
このように、法則性を見つけて、減らしていきます。

ただ、入力が多くなると、表を眺めていてもなかなか気づきにくいことがあります。
それを考える時のアイテムのひとつにベン図が便利です。

# ベン図

ベン図について、詳しくは[こちら](https://ja.wikipedia.org/wiki/ベン図)

私は高校の数学で習ったので、
同じく見たことある方も多いと思うのですが、
ベン図を使うことで視覚的に整理することができます。

|入力A|入力B|入力C|出力D|
|:----|:----|:----|:----|
|false|false|false|false|
|false|false|true |false|
|false|true |false|true |
|false|true |true |true |
|true |false|false|false|
|true |false|true |false|
|true |true |false|true |
|true |true |true |true |

先程のこの表もベン図を書いて見ると、すぐにわかります。

|入力A|入力B|出力C|
|:----|:----|:----|
|false|false|false|
|false|true|true |
|true |false|true |
|true |true |false|

例えば、この表は、
入力Aと入力Bのどちらかだけがtrueであれば、入力Cがtrueになる
排他的論理和(XOR)です。
ただ、表を眺めているだけだと気づきにくかったりするのですが、
ベン図を書いて見ると、すぐにわかります。

ただ、同時に入力３つを超えると描きにくいため、不便な面もあります。

## 考える時のツール

真理値表、ベン図を使うと整理しやすくなります。
さらに、カルノー図、論理式、ブール代数などを使うことで、考える幅を広げられます。

ただし、やりすぎは危険です。
正規化と同じで、やりすぎると元々の形がわかりにくくなりますし、
変更に対してとても弱くなってしまいます。

また、現実的な仕事の中だと、
数学や論理的に証明できる範囲では減り切らずに、
組み合わせが膨大になってしまうことがあるため、
あくまで、試験を考える時のツール（道具）のひとつだと思って使います。

（組み合わせが膨大になる場合、例えば、直交表などを使って減らしたり）

# ３．ログを見る
## 障害解析時に使えるログになっているか
単体試験で試験しながらログを眺めます。
そして、障害解析時に使えるログになっているか？を考えます。

特に、ログレベルは大切で、DEBUGレベルだとエラー内容が出るけど、
運用時のINFOレベルでは、エラー内容が出ないことがあります。

本番環境で問題が起来た時に「ログ見てもわからない！」となるのは悲惨です。

「何が起きたか」がトレースできるログであることは大切です。
アクセスログや対向システムとの通信内容はできる限り、ログに残しておと障害解析に役立ちます。
## 匂いを感じる

ログを見ていると、「あれ？これ何かおかしい？」と不具合の匂いを感じることがあります。

ぼーっと見ているだけだと、気づかないこともありますが、
嗅覚を研ぎ澄ませてログを眺めていると、予期せぬ不具合に気づくことがあります。
じーっと一行一行を見ることもあれば、俯瞰することもあります。

# ４．どうすれば動かなくなるか？
単体試験を試験設計をする時は、「どうすれば動かなくなるか？」をひたすらに考えていました。
#### 意地悪な入力
- XSS、サロゲートペア、ディレクトリトラバーサル
- 存在しないIDを入力
- URLに直接遷移する
- 必要なファイルを消しておく

#### 途中で何が起きたら？
- DBが再起動する
- ネットワークが切れる
- プロセスが落ちる
- トランザクション中に例外が起きたら、ロールバックされる？

#### 大量データ処理や連続実行をして大丈夫か？
- レイテンシの低下、SQLが遅い
- 処理輻輳
- メモリリーク
- マルチスレッド
- 同時実行

#### 設計や実装の難しい部分で、どういうところに設計や実装の穴があるか？
特に大切。システムの穴を探します。

- 考慮が漏れそう
- 処理のタイミングで矛盾が起きそう
- 設計が難しそう
- ソースコードを見ると怪しい
- ログに気になるものが出ている
- セキュリティ

例えば、状態を管理しているシステムでは、
処理の輻輳時に、想定外のステータス遷移をして想定外の動きをする、なんてことはあります。
人間が考えていることなので、もちろん穴があります。
思考が薄くなりやすいポイントを考えて狙いに行きます。

## 良いシステム？
以上のように、意地悪な試験をするために、考えていました。

背理法的証明の考え方に近いと思うのですが、
システムの正しい動きを考えるのではなく、
逆に、「システムはどうすれば動かなくなるか？」を考えて、
考え抜いた先に、何もなければ、それはきっと良いシステムです。
...というのは、さすがに強引ですね^^;

## 動きを把握しておく


システムを運用していくと、思いもよらないことは起きます。
ユーザが思っても見ない操作をしたり、サーバが落ちたり、様々です。
いわゆる正常系、ではなく、異常系、という動作は様々です。

試験をする過程で、
事前にシミュレートしておいたり、システムの動きを把握しておくことで、
何か起こった時に、
「これは見たことがある、あれかな？」と気付く、
「○○の時は××のように動くから、この仮説の可能性は低い」と切り分けの参考にできることがあります。
## 単体試験までに見つけたほうが良い
後工程や本番環境で問題が起きるといろいろと大変です。

異常系の処理は、アーキテクチャの見直しが必要なこともありますが、
開発工程が進むほど直すことは難しくなり、
偉い人への報告や水平展開にも追われてします。

また、一度本番環境で動いてしまえば、動くものを直すハードルはとても高くなります。

できるだけ、単体試験で見つけて直しておきたいものです。
# まとめ
1. 「[プログラマのための数学](https://www.amazon.co.jp/dp/4797395451)」はオススメ
1. 試験と数学の証明は近い部分がある
1. 真理値表とベン図を考える時のツールに使う
1. ログを見て匂いを感じとる
1. どうすれば動かなくなるか？を考えて、穴を狙う

単体試験をする時に考えていたことを書いてみました。
書いた後に気づきましたが、まとまりがない内容になってしまいました。
（それにしても、昔は今と比べてちゃんと単体試験してたな...）


ここまでお読みいただきありがとうございました。