ユニットテストとアサーションをどう扱うのか?
初心者のために簡単に説明すると、バグを減らすために使用する技法です。大体こんな感じでアサーションを使用します。
#include <assert.h>
int foo( int value )
{
int result;
//事前条件
//引数は0以上でなければならない
assert( value >= 0 );
//何らかの処理をして戻り値を導出する
result = value;
//事後条件
//返す値は-1以上でなければならない
assert( result >= -1 );
return result;
}
int main()
{
//制約違反!
//foo( -10 );
//OK
foo( 0 );
return 0;
}
これはC言語を使った非常に簡単な例です。実務でこの様な関数は使用しませんが、雰囲気は伝わると思います。テストにはもうひとつ、有名かつ頻繁に利用される技法があります。それは、ユニットテストです。大半の人がユニットテストを書いている事でしょう。特にJavaプログラマは殆どの人がJUnitを使用していると思います。
ここで一つの疑問が生じます。アサーションとユニットテストをどのように扱うのかです。アサーションを使用した技法は一見、ユニットテストと相性が合いません。事前条件、事後条件、不変表明の3つのチェックをソースに一切書かず、テストプログラムに記述すれば可読性が向上して良いと考える人がいても当然です。特にJavaや.NET系の言語は例外処理を使用する事もあり、アサーションをどのように使用するのか悩む人が多いでしょう。
私も悩んだのですが、今は両方の技法を使用した方がいいと結論付けています。その理由は設計意図とテストプログラムは別種のものだからです。プログラムに設計の意図を書くのと、テストプログラムを書くのは別の意味を持っている行為です。
まず、アサーションの方ですが、これは設計の意図を示すものです。ただ単にエラーが発見できればいいというものではなく、設計者の意図を示すのが本意です。関数もしくはメソッドがどういった制約を持つのかは、明らかに設計に属する事柄なのです。
一方、テストプログラムはバグの撲滅が本意です。テストプログラムを書くと、自然とオブジェクトデザインが明らかになったりしますが、それは副次的な効果であり、テストはバグをなくすために行う行為です。むろん下手なインタフェースはバグの元であり、テストとオブジェクトデザインは無関ではありませんが、だからと言って両方を詰め込むのは頂けません。集団作業で本来の意味とは違うものを詰め込むと、コミュニケーションを阻害する恐れが高いです。コミュニケーションに支障が生じると、バグの発生件数が多くなるのは言うまでもないでしょう。
以上の考えから私は、設計の意図とテストを明確に分離するために、2つの技法を併用しています。ただし、プリプロセッサを併用しなくてはなりません。それが少し美しくないのですが、アサーションを使用した報告機能を用意しておくと、万が一運用時にバグが生じても解決が速くなるので利点があります。運用時に1つのバグもないのが理想ですが、絶対安全神話を唱えずに、もしバグがあっても解決できるように準備しておくのが当然だと言えるでしょう。
この話題に限らず、分離性は大事だと時だと私は考えております。物事を分離しておく方が明瞭かつ単純に保てます。先の事を考えるのであれば、明瞭であるべきだというのが私の持論なのです。この記事が誰かのお役にたてば幸いです。
※注意:.NETの場合はアサーションではなくコードコントラクトを使用するべきです。