コードでenum
型をスイッチと一緒に使用して、各型にロジックを適用することがよくあります。これらの場合、各enum
にコードを実装することが重要です。
例えば;
_public enum eERROR
{
REQUIRED,
DUPLICATE,
UNKNOWN,
MISSING_VALUE
}
public void Error(eERROR pError)
{
switch (pError)
{
case eERROR.REQUIRED:
// ......
return;
case eERROR.DUPLICATE:
// ......
return;
case eERROR.MISSING_VALUE:
// ......
return;
case eERROR.UNKNOWN:
// ......
return;
}
throw new InvalidArgumentException("Unsupported error type");
}
_
一番下に、プログラマーがenum
ステートメントに新しいswitch
タイプを追加することを思い出した最後の手段として例外を追加しました。新しいタイプがeERROR
に追加された場合、コードが更新されていないと、例外がスローされます。
これが私の問題です。
上記のコードは99%のみの単体テストカバレッジを生成します。例外をトリガーできないからです。ジェネリックunhandled
をeERROR
に追加してError(eERROR.unhandled)
を呼び出すだけで100%カバレッジを取得できますが、何かを解決するためのハックのように感じます完全にカバーするだけでは問題ではありません。行を削除することはできますが、安全性チェックはありません。
99%が問題なのはなぜですか?コードベースが非常に大きいため、カバーされていないメソッドはカバレッジを98%に移動しません。したがって、私は常に99%を参照し、メソッドを1つまたは2つ逃したかどうかはわかりません。
どのようにError()
を書き換えて、スイッチに追加されていないすべての新しく追加された列挙型がプログラマーによって何らかの方法でキャッチされ、テストによってカバーされるようにすることができますか。ダミー列挙型を追加せずにそれを行う方法はありますか?
言語によっては、ガベージ値をErrorコンストラクターに渡すことができるはずです。ほとんどの言語は、列挙値に整数を使用します。疑似コード:
e = new Error(eError -1)
>> InvalidArgumentException: Unsupported error type
日々テスト駆動開発を適用する開発者として、私は問題が逆であるべきだと信じています。コードをテストできない場合、そのコードの理由はありません。可能であれば、継承によって列挙型を拡張するか、モックを使用して特定のケースをトリガーできます。
あなたはできないプログラミングエラーが発生した場合にのみ実行することを目的としたコードをテストします。これは、コードベースにプログラミングエラーがある場合にのみテストスイートが正常に実行できることを意味します。それにもかかわらず、おそらく不可能であるケースに対してそのようなガードを作成することが有用であるという事実は、「絶対に100%のコードカバレッジ」という目標が良い目標ではない理由の1つです。
1つの質問で、すべてをキャッチする句が必要ですか? Enumに新しい要素が追加された場合、タイプチェッカーはそれをキャッチしますか? Erlangでそのcaseステートメントのようなものを書いた場合、ダイアライザはエラーのケースが静的分析で一致することは決してあり得ないことをはっきりと伝えます。
多分私はすべての言語に存在しない非常に優れたツールに甘やかされています。
これについてはどうでしょうか。次に、null
を無効な値としてテストできますか?
public void Error(eERROR pError)
{
if (pError != null) {
switch (pError)
{
// ......
}
}
throw new InvalidArgumentException("Unsupported error type: " + pError);
}