テストユニットでエラーを検証する

前提

ユニットテストで正しいExceptionが発行する方法がわからない人。期待値にExceptionを求める意味が分からない人向け。

■ExceptionをすべてExceptionだけで片付けてはいけない

Webだと「まぁいいかな」と妥協できるが、クラサバ型のソフトやちょっとしたツール類のようなEXE形式のプログラムの場合は、Exceptionはきちんと分類して法がいい。つい先日、運用レベルのソフトで痛感させてもらった。

「バッチ処理でエラーのメールが飛んできたんだけど」ここが事の発端だった。実運用ではたびたび起こること。それほど珍しくはない。よくよく話を聞いてみると、どうもDBサーバーとのハンドシェイクに失敗してしまったようだ。インフラ担当者に問い合わせてみたが、DBサーバー側にダウンタイムはないとのこと。こうなると、原因の特定がなかなか難しくなる。珍しいこともあるもんだ、と流していたのだが、改修の方針を聞いてみると、ハンドシェイクに失敗したらアプリ側からリトライをかける、とのこと。当然、ちょっと待ちなさい、となった。

バッチ処理はWindowsのタスクで管理されている。エラーになったのだから、タスクから自動で再実行すれ抱けばいいはず。。。なのだが、バッチは「正常終了」しているとのこと。なんのこっちゃ、となるのは当然だ。エラーメールが発行されているにもかかわらず、exeとしては正常終了しているのだから。

Web系ばかりに携わっているうちに、クラサバ型の考え方についていけなくなったのでは、と不安になってしまった。自分が運用系のツールソフトを作っていた際は、エラーが体系化されていて、どんなに小さなバッチプログラムでもExceptionごとに違うErrorCodeを充てていた。Throw Exception はいわば、最後の砦的な、どうにもわからないエラーが出たときに入ってくるElse的な処理だったはず。そこにすら至っていないというのはどういうことだろう?

ちょっと自信が持てなかったので、知人各位に聞いてみたところ、その辺の考え方はさほど変わっていないようだ。安心したのもつかの間、このままでいいわけがない。で、ExceptionをExceptionだけで終わらせてはいけないというくだりになるわけだ。

余談が長くなったが、細部にわたって、とまではいわないが、ある程度の体系化を持たせて、きちんとErrorCodeを返すようにしよう。

Environment.ExitCode

とすればいいだけなのだから。

テストの実装

ソースは下記となる。
AreEqualでエラーメッセージを検証している。

    <TestMethod> Public Sub テスト()
        Dim ex As Exception = Assert.ThrowsException(Of NotImplementedException)(Sub() アクション名())
        Assert.AreEqual(ex.Message, "メソッドまたは操作は実装されていません。")
    End Sub

    Private Sub アクション名()
        Throw New NotImplementedException()
    End Sub

舌の根も乾くぬうちにExceptionかよ、と思った人もいるかもしれないので言い訳をしておく。exをExceptionとしたのは、あえてそうしている。NotImplementedExceptionとしてしまうと、具象化しすぎるのでテストの意味が薄いと感じたからだ。あえて抽象的に受けておいて、AreEqualで具体的に比較を行いたかった。

ここは賛否両論あると思うので、あえて深くは突っ込まない。

まとめ

Exceptionのテスト自体はさほど難しいことではない。ただ、きちんとエラー処理の振り分けを行うとなると、エラーコードのレベルから考える必要がある。自分たちが購入したソフトがあったとして、エラーコードがきちんと表示されるソフトであれば、エラーコードをもとにサポートに問い合わせることができる。何もないと、事象から説明しなければならないし、ソフト内にひそかに設置されているログをもって問い合わせる必要が出てくるかもしれない。

それでよい、としてしまう強気な態度も時には必要だが、異常を起こしているのはソフト側なのだから、ここはユーザービリティを優先すべきところだろう。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です