読者です 読者をやめる 読者になる 読者になる

「気を抜かないように気をつけて」テストする

昨日は疲れていたのでこんなてきとうな精神論を掲げて寝ました。

たった5分10分のテストが1つ抜けただけでその数十、数百倍の時間を取られてしまうことを考えると、テストするときは本当に気を抜けないな、とあらためて思いました。

気を抜かないよう心がけてバグが無くなるなら世の中にバグなんてものは存在しないわけで、精神論で品質が向上しないことぐらいはこの1年で学びました。「気を抜かないように」なんて言っている時点で無意識のうちにそれが起こりうることは明らかで、当然「気をつける」という行為の中で気が抜けてしまうこともありえるから自己矛盾しているわけです。

まあそんな言葉遊びはどうでも良くて、うちの会社ではテスターというのが存在せず、プログラマーが単体テストを書いて、結合テストをして、動作確認テストをします(品質保証部門というのはあって、そこでも一通りの動作確認テストはしてくれます)。そんな中で、僕がこれまでやってきたテストでどういうエラーを回避できるのかまとめてみようと思います。

  単体テスト(プ) 単体テスト(テ) 結合テスト 動作確認テスト
構文エラー
意味エラー
解釈エラー ×
仕様エラー × ×

名前は僕がてきとうに付けたモノですので、以下にそれぞれのパターンを説明します。

構文エラー

説明するまでもないですがプログラムが動かないパターンです。

a = 2 + * 3

構文エラーはコンパイル言語であればコンパイル時に、スクリプト言語であれば実行時にエラーが存在することがわかるので、少なくとも単体テストを行うタイミングでは発見できます。

意味エラー

仕様:「0以上の値を渡されたらTrueを返し、負の値を渡されたらFalseを返す」を渡されたときにプログラマが"="を「入れ忘れて」以下のような関数を書いたとします。

def test(param):
  if param > 0:
    return True
  else:
    return False

これに対応する単体テストを書く場合、例えば次のようなテストを書くことで、プログラマ自身が単体テストを書いてもエラーを回避することが出来ます。

assert test(-1) == False
assert test(0) == True
assert test(1) == True

解釈エラー

先ほどの仕様を読んだプログラマがもし「0より大きければTrueを返し、0以下であればFalseを返す」関数であると勘違いした場合はどうなるでしょう。プログラマ自身が単体テストを書くと以下のようなものになるはずです。

assert test(-1) == False
assert test(0) == False
assert test(1) == True

数百・数千という関数を書いていく間に仕様を勘違いすることなど十分ありえます。しかも、一度勘違いしたものを自分自身で再度正しいモノで修正するのは難しいので、自身で単体テストを書く場合はエラーを免れるのは難しいです。この場合、もしテスターがいれば仕様通りにテストを書いてくれればエラーを発見することが出来ます。もちろん、テスターも勘違いすればそのままですが、1人が勘違いする確率よりも2人が同じように勘違いする確率はぐっと下がります。

仕様エラー

そして設計者がプログラマに渡す仕様書が間違っていたら、単体テストではどうにもなりません。複数のモジュールを結合させてテストしたときに、関数AからTrueが返ってくることを期待している関数Bでおかしなことがわかります。
もちろん、この仕様エラーが各モジュールを結合したレベルで存在していれば、このエラーを発見することもできません。実際にサービス提供状態で動かしたときに、ようやく「あれ?この動きおかしくね?」ということになるはずです。

テストの工数

すべてのパターンについて動作確認テストや結合テストを実施できれば良いのですが、上記の表で右に行くほどテストの工数が大きくなるので、現実的には難しいです。例えば10個の関数が存在して、それぞれ内部で10個の分岐があるとします。単体テストで1個ずつの関数をカバーするには10パターンずつ×10関数で100個テストすれば良いですが、全部を結合させてテストする場合、単純に計算すると10の10乗=10000000000個のテストになります。もちろん実際はその中から意味のあるものに絞り込んでテストするわけですが、絞り込む作業自体も大変です。
とか言いつつ結合テストもここ最近少しやっただけで、どのようにテストパターンを網羅するのがいいのかはほとんどまったく分かっていません。



というわけで、この1年で僕がテストについて感じたことはこんなもので、少なくとも自分で書いた単体テストは信用できないモノとして扱うことにしました。ただし、テストのための時間もあまりなく、他者に単体テストしてもらうことはできませんし、結合テストをみっちりやることも難しいです。厳しい現状ですが、エラーが見つかればその障害対応に追われることになるのでそれは当然避けたいです。
現場の先輩からテストについて教えてもらうのも難しそうなので、これからいくつか本を読んでみようと思います。

現場の仕事がバリバリ進む ソフトウェアテスト手法

現場の仕事がバリバリ進む ソフトウェアテスト手法