ユニットテストを書かなかった頃の自分に言い返してみる

コードを実装したときにテストはどのようにしていますか?

ユニットテストは書いていますか?

ユニットテストは単純なロジックを確認するだけではなく、条件分岐の網羅や境界値テストなどを繰り返し、行い続けられることが強みです。一度、書いてしまえば、手間なく何度もテストができます。それによるメリットは使い続けてこそ、強く分かってきます。

ユニットテストは書かないという人もいらっしゃると思いますので、書かなかった頃の自分の場合の理由を挙げたいと思います。そして、それに対して言い返してみます。もし、同じ理由で書いていないという方がいらっしゃいましたら、試しに取り組んでみてください。きっと続けたくなりますよ。

スポンサーリンク

ユニットテストを書かない理由

少し前まで、私はユニットテストをあまり書かない方でした。書かなかった頃の理由は次のようなものでした。

  • ユーザーインターフェイスのコードが中心でそもそも書きづらい。
  • ハードウェアと通信するコードが多くハードウェアがないと動かない。
  • ネットワークと通信するコードが多くサーバー相手に変なデータを送信するのは怖い。
  • 単純な処理を確認するだけならデバッガで十分だと思っていた。
  • そもそも時間がもったいない。

書こうと思えば、もっと書けると思いますが、今から見ると、全て言い訳です。ここで過去の自分に言い返してみようと思います。同じような理由で書いていない読者の方もいらっしゃると思います。

ユーザーインターフェイスのコードが中心でそもそも書きづらい

UIテストを使うべき。

XcodeにはUIテストを行う機能があります。画面上からウインドウやボタンを見つけて、クリックして、どのように画面が遷移するかテストすることができます。

私も実際に一つのアプリの画面遷移を全てUIテストで書いてみました。初めて書いたときは時間がかかりましたが、書けるものです。

UIテストの利点

ロジック側の変更やリファクタリングによって内部構造が変化したときに、ユーザーインターフェイスに悪影響が出ていないかを繰り返しテストできます。特に次のような場所は手動テストでは盲点になりやすいです。

  • 複数のチェックボックスをチェックしたときの、組み合わせによるボタンの状態変化(有効・無効など)
  • 特定のタイミングでは無効になるべきコントロールが無効になっているか
  • 特定のタイミングではアプリの終了を許可していないことを確認する
  • 画面上の目立たない場所の変化。例えば、表示するテキストの変更など

ハードウェアと通信するコードが多くハードウェアがないと動かない

ハードウェア制御系のプログラムで多いケースだと思います。しかし、実機を使ったテストではエラーパターンは逆にテストできないや、テストデバイスの制限範囲でしかテストできないというデメリットがあります。

制御系では、入力と出力は仕様で定義され、仕様書通りに動作するハードウェアを前提にすれば、テストするべきは以下の2点です。

  • 実機に送信するデータの作成処理
  • 実機から受信したデータの解析処理

データの作成処理のテスト

データの作成処理のテストは、入力値の境界値、中間値を用意して、送信するデータを作成します。

作成したデータが仕様書通りの形式になっているかをユニットテストでテストします。

例えば、先頭4バイトが入力値になっている、次の4バイトが入力値に対する特定の値になっているなどです。ユニットテストのコードで、バイト単位で値を取り出して、テストします。

このとき、解析処理を実プログラム側に用意して同じものを使うとかはダメです。テストされる側のコードで確認処理をしていたらテストになりません。

実機から受信したデータの解析処理

実機と通信する処理をモックに置き換えて、指定したデータを返すようにします。指定するデータは以下のものです。

  • 仕様書通りのデータ
  • 仕様書通りの境界値
  • エラー値

実機だとテストしたい値にするのは難しいので、モックを使ったユニットテストの方が強いです。取り得る値の網羅テストだってできてしまいます。

でも結局通信処理はテストできていないが

実機との本当の通信についてはテスト出てきていません。しかし、実機に送信するデータが正しいことが保証され、受信したデータは正しく処理されることが保証されれば、実際に操作してテストしなければいけない範囲は非常に狭くなるのではないでしょうか?

ネットワークと通信するコードが多くサーバー相手に変なデータを送信するのは怖い

これもハードウェアテストと同じです。本当に通信する必要はありません。テストするべきは、以下の2つです。

  • 送信するデータの作成処理
  • 受信したデータの解析処理

モックもHTTP用なら高機能なものがあります。

また、少し賢いモックを用意すれば、データの送信される順番も色々な組み合わせにして、テスト対象のコードが受信するデータの順番が狂っても動くかをテストできます。このとき、ランダムにしたはダメです。再現できないテストはあまり役に立ちません。(順番をログに記録していて、再現できるなら良いと思いますが)

単純な処理を確認するだけならデバッガで十分だと思っていた

確かにそうですね。ただ、単純な処理も何度もたくさんあると大変です。そして、それを使っているところや単純な処理の内部で使っている処理を変更したときに、全てが正しく動くかをテストするのは、デバッガで目視ではつらいです。つらいと見なくなります。

特にアルゴリズムや基本エンジンの中など、無数の処理が何度も使っているところを変更するのは、単純なコードの単純な変更でも怖いです。疲れていると、なぜこんなミスをしたんだと後で思うようなことも起こります。

なぜ、あのとき、プラスとマイナスを間違えたんだ。なぜ、あのとき、1桁間違えたんだとか。色々と悲しさと恥ずかしさが混じった記憶がよみがえります。

そもそも時間がもったいない

取り組んでいないときは常に思っていました。単純にテストコードを書いて、それをメンテナンスするのは大変です。しかし、トータルで考えると、結合試験やシステム試験で不具合が見つかって、修正するときの時間を考えると、どっちが短いか分からなくなります。

繰り返しできることを考えると、将来、起こりえた不具合も潰しているので、継続していくプロジェクトであればあるほど、効果は大きいと思います。

書いたテストは繰り返し実行しているか?

継続的インテグレーションはまだ導入していないので、Xcode上で繰り返し実行しています。

実行するタイミングはリファクタリングを行ったときだけではなく、イシューを解決して、プルリクを作成する前などです。変更が他の処理に影響を与えていないことを確認して、安心しています。

ユニットテスト導入前は、実行して一通り操作してみて、ということを繰り返していました。

スポンサーリンク
最新情報をチェックしよう!
>現役のプログラマーが書くプログラミング情報

現役のプログラマーが書くプログラミング情報

日々の開発の中での学びや分かったこと、調べたことなどを書いていくブログです。

CTR IMG