web-dev-qa-db-ja.com

単体テストを使用して列挙型の値をテストする必要がありますか?

値のみの列挙型(Javaで実行できるメソッドがない)があり、この列挙型がシステムのビジネス定義の一部である場合、ユニットテストを作成する必要がありますか?

私はそれらが記述されるべきだと思っていました、たとえそれらが単純で冗長に見えるかもしれませんが、ビジネス仕様に関することは、ユニット/統合/ ui /などであるかどうかにかかわらず、テストで明示的に書かれるべきだと思います。テスト、またはテスト方法として言語の型システムを使用する。列挙型(Javaなど)に必要な値は、ビジネスの観点から、型システムを使用してテストできないため、そのための単体テストがあるはずだと思います。

この質問は私のものと同じ問題を扱っていないため、 これ とは似ていません。その質問にはビジネス関数(savePeople)があり、その人は内部実装(forEach)について問い合わせています。そこには、言語構造(forEach)をカプセル化する中間のビジネス層(人を救う機能)があります。ここで、言語構造(列挙)は、ビジネスの観点から動作を指定するために使用されるものです。

この場合、実装の詳細はデータの「真の性質」、つまり(数学的な意味で)値のセットと一致します。間違いなく不変セットを使用することもできますが、同じ値がまだ存在しているはずです。配列を使用する場合は、ビジネスロジックをテストするために同じことを行う必要があります。ここでの難問は、言語構造がデータの性質と非常によく一致するという事実です。私が自分自身を正しく説明したかどうかわかりません

16
IS1_SO

値のみの列挙型(Javaのようにメソッドがない)があり、この列挙型がシステムのビジネス定義の一部である場合、ユニットテストを記述する必要がありますか?

いいえ、彼らはただの状態です。

基本的に、列挙型を使用しているという事実は実装の詳細;です。これは、別のデザインにリファクタリングできるようなものです。

列挙型の完全性をテストすることは、表現可能な整数がすべて存在することをテストすることに似ています。

ただし、列挙型がサポートする動作をテストすることをお勧めします。つまり、合格したテストスイートから開始し、単一の列挙値をコメントアウトすると、少なくとも1つのテストが失敗します(コンパイルエラーは失敗と見なされます)。

40
VoiceOfUnreason

列挙型declarationはテストしません。関数の入出力に予期される列挙値があるかどうかをテストできます。例:

enum Parity {
    Even,
    Odd
}

Parity GetParity(int x) { ... }

あなたはしないでくださいテストを記述してから、列挙型ParityEvenOddの名前を定義していることを確認します。コードで既に述べられていることを繰り返すだけなので、そのようなテストは無意味です。同じことを2度言っても、正しいことにはなりません。

あなたはdoGetParityを検証するテストを記述して、0の場合はEvenを、1の場合はOddを返します。コードを繰り返すのではなく、実装に関係なくコードの動作を検証しているため、これは貴重です。 GetParity内のコードが完全に書き換えられた場合でも、テストは有効です。実際、単体テストの主な利点は、コードが期待どおりに機能することを保証することにより、コードを安全に書き換えてリファクタリングできる自由を与えることです。

ただし、列挙型declarationが予期される名前を定義することを確認するテストがある場合、将来列挙型に変更を加えると、テストも変更する必要があります。これは、作業量が2倍になるだけでなく、単体テストのメリットが失われることも意味します。 同時にでコードを変更してテストする必要がある場合、バグの導入に対する安全策はありません。

17
JacquesB

列挙型を変更するとコードが壊れるリスクがある場合は、C#の[Flags]属性を持つものはすべて2から4(3)の値を追加するのがビットごとの1と2ではなく、控えめなアイテム。

保護のレイヤーです。

すべての開発者が使い慣れている列挙型の行動規範を検討する必要があります。列挙型のテキスト表現に依存しないことが一般的ですが、これはシリアライゼーションのガイドラインと競合する可能性があります。

Enumエントリの大文字の使い方を「修正」し、アルファベット順に並べ替えたり、他の論理グループごとに並べ替えたりして、すべてが不正なコードの他のビットを壊したのを見てきました。

11
Ian

いいえ、列挙型にすべての有効な値が含まれていて、本質的に列挙型の宣言が繰り返されていないことを確認するテスト。意味のないテストであるenumコンストラクトが言語で適切に実装されていることをテストするだけです。

つまり、列挙値に依存する動作をテストする必要があります。たとえば、列挙値を使用してエンティティをjsonなどにシリアル化する場合、またはデータベースに値を格納する場合は、列挙型のすべての値の動作をテストする必要があります。このようにして、列挙型が変更された場合、少なくとも1つのテストが失敗します。いずれにしても、テストするのは列挙型の宣言自体ではなく、列挙型の周囲の動作です。

11
jesm00

コードは、列挙型の実際の値とは無関係に正しく機能する必要があります。その場合、単体テストは必要ありません。

ただし、列挙値を変更すると問題が発生するコードがある場合があります。たとえば、enum値が外部ファイルに格納されている場合、enum値を変更した後、外部ファイルを読み取ると誤った結果が返されます。その場合、値を変更しないようにだれにも警告する列挙型の近くに大きなコメントがあり、数値をチェックする単体テストを書くこともできます。

3
gnasher729

一般に、列挙型が値のハードコーディングされたリストを持っていることを確認するだけでは、他の回答が言っているように、それほど価値がありません。

私はかつて、1つのモジュールが他の2つのモジュールの列挙型を使用し、それらの間でマッピングしたというケースがありました。 (列挙型の1つには追加のロジックがあり、もう1つにはDBアクセス用でした。両方とも、互いに分離する必要のある依存関係がありました。)

この場合、ソース列挙内のすべての列挙エントリがターゲット列挙にも存在する(したがって、マッピングが常に機能する)ことを確認するテストを(マッピングモジュールで)追加しました。 (場合によっては、その逆もチェックしました。)

このように、誰かがenumエントリをenumの1つに追加し、対応するエントリを他のenumに追加するのを忘れた場合、テストは失敗し始めました。

1
Paŭlo Ebermann

列挙型は、カスタムの(うまくいけば意味のある)名前を持つ単純な有限タイプです。 enumは、voidだけを含むnullのように、1つの値のみを持つ場合があります(一部の言語ではこれをunitと呼び、en_の名前にvoidを使用します。 no要素!) boolfalseを含むtrueのように、2つの値を持つ場合があります。 colourChannelredgreenblueのように、3つある場合があります。等々。

2つの列挙型が同じ数の値を持つ場合、それらは「同型」です。つまり、すべての名前を体系的に切り替えると、ある名前を別の名前の代わりに使用でき、プログラムの動作が変わることはありません。特に、testsの動作は変わりません!

たとえば、resultを含むwin/lose/drawは、上記のcolourChannelと同型です。 colourChannel with resultred with wingreen with loseおよびblue with draw、そしてそうする限りeverywhere(プロデューサーとコンシューマー、パーサーとシリアライザー、データベースエントリ、ログファイルなど)であれば、プログラムに変更はありません。 colourChannelがなくても、私たちが書いた「colourChannelテスト」はすべて合格します。

また、enumに複数の値が含まれている場合は、常にrearrangeそれらの値を使用して、同じ数の値を持つ新しいenumを取得できます。値の数は変更されていないため、新しい配置は古い配置と同型であり、したがってすべての名前を切り替えることができ、テストは引き続き合格します(ただし、できないだけに注意してください)定義を切り替えます。すべての使用サイトも切り替える必要があります)。

これが意味することは、マシンに関する限り、列挙型は「識別可能な名前」と他に何もないであるということです。 enumで実行できる唯一のことは、2つの値が同じか(例:red/red)または異なるか(例:red/blue)。したがって、「単体テスト」で実行できるのはそれだけです。

(  red == red  ) || throw TestFailure;
(green == green) || throw TestFailure;
( blue == blue ) || throw TestFailure;
(  red != green) || throw TestFailure;
(  red != blue ) || throw TestFailure;
...

@ jesm00が言うように、そのようなテストはプログラムではなく言語の実装をチェックしています。これらのテストは決して良い考えではありません。言語の実装を信頼していない場合でも、テストする必要があります外部から。テストを正しく実行することは信頼できないためです。

それが理論です。練習はどうですか?列挙型のこの特徴付けの主な問題は、「現実の世界」のプログラムが自己完結型であることはめったにないことです。レガシーバージョン、リモート/埋め込み展開、履歴データ、バックアップ、ライブデータベースなどがあるため、実際に「切り替え」することはできません。いくつかの用途を見逃さない名前のすべての出現。

しかし、そのようなことは列挙型自体の「責任」ではありません。列挙型を変更するとリモートシステムとの通信が切断される可能性がありますが、逆に、列挙型を変更することでこのような問題が発生する可能性がありますfix

そのようなシナリオでは、列挙型は赤字です:1つのシステムがthis wayである必要があり、別のシステムがthat wayである必要がある場合はどうなりますか?いくつのテストを書いても、両方になることはできません!ここでの本当の原因は入出力インターフェースであり、「解釈によって選択される整数」ではなく、明確に定義されたフォーマットを生成/消費する必要があります。したがって、本当の解決策はi/oインターフェースをテストする:期待されるフォーマットを解析/出力していることを確認する単体テストと、フォーマットが実際に相手側で受け入れられていることを確認する統合テストとです。 。

列挙型が「十分に行使されている」かどうかはまだ疑問に思われるかもしれませんが、この場合、列挙型は再び赤いニシンです。私たちが実際に心配しているのはテストスイート自体です。ここでは、いくつかの方法で自信を得ることができます。

  • コードカバレッジは、テストスイートからのさまざまな列挙値がコードのさまざまなブランチをトリガーするのに十分であるかどうかを教えてくれます。そうでない場合は、カバーされていないブランチをトリガーするテストを追加したり、既存のテストでさまざまな列挙を生成したりできます。
  • プロパティチェックは、コード内のさまざまなブランチが実行時の可能性を処理するのに十分であるかどうかを教えてくれます。たとえば、コードがredのみを処理し、redでのみテストする場合、カバレッジは100%になります。プロパティチェッカーは、アサーションの反例(テストするのを忘れたgreenおよびblue値の生成など)を(試行しようと)生成します。
  • 変異テストは、アサーションが実際にcheck列挙型であるかどうかを、ブランチだけを追跡してその違いを無視するのではなく、知ることができます。
1
Warbo

いいえ。ユニットテストはユニットをテストするためのものです。

オブジェクト指向プログラミングでは、ユニットは多くの場合、クラスなどのインターフェース全体ですが、個別のメソッドの場合もあります。

https://en.wikipedia.org/wiki/Unit_testing

宣言された列挙型の自動テストは、開発者が作成したコードのロジックではなく、言語とそれが実行されているプラ​​ットフォームの整合性をテストすることになります。列挙型を宣言するコードは、それをテストするコードと同様にドキュメントとしても機能するため、ドキュメントは含まれていますが、それは有用な目的には役立ちません。

1
digimunk

コードの観測可能な動作、観測可能な状態に対するメソッド/関数呼び出しの影響をテストする必要があります。コードが正しいことを実行している限り、他に何もテストする必要はありません。

クラスが実際に存在すること、またはクラスに期待するメソッドと属性があることを明示的に表明しないのと同じように、列挙型に期待するエントリがあることを明示的に表明する必要はありません。

実際に動作をテストすることにより、テストに関係するクラス、メソッド、および値が存在することを暗黙的に表明しているため、明示的に表明する必要はありません。

正しいことを行うためにコードに意味のある名前を付ける必要はないことに注意してください。これは、コードを読む人にとって便利なだけです。 foobar...などの列挙値とfrobnicate()などのメソッドを使用してコードを機能させることができます。

0