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


15

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

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

この質問は類似していない、この1、それは私と同じ問題に対処していないので。その質問にはビジネス機能(savePeople)があり、その人は内部実装(forEach)について問い合わせています。そこには、言語コンストラクト(forEach)をカプセル化する中間ビジネスレイヤー(人を救う機能)があります。ここで、言語構造(enum)は、ビジネスの観点から動作を指定するために使用されるものです。

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


19
列挙型の単体テストは正確にどのように見えるでしょうか?
jonrsharpe


@jonrsharpe列挙内にある値は、期待したものであると断言します。列挙型の値を反復処理して、たとえば文字列としてセットに追加することでそれを行います。そのセットを注文します。テストで手書きで書き込まれた値の順序付きリストと比較するセット。それらは一致するはずです。
IS1_SO

1
@jonrsharpe、ユニットテストは、コードで記述された「定義」または「要件」とも考えます。列挙型の単体テストは、列挙型のアイテムの数とその値をチェックするのと同じくらい簡単です。特にC#では、列挙型はクラスではありませんが、整数に直接マップでき、それらの値を保証し、偶然によるプログラミングではなく、シリアル化の目的に役立ちます。
マチャド

2
私見では、宇宙が正しいかどうかを確認するために2 + 2 = 4であるかどうかをテストするよりもはるかに便利ではありません。列挙自体ではなく、その列挙を使用してコードをテストします。
Agent_L

回答:


39

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

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

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

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

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


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

4
@I その場合、Enumを特にテストする必要はありません。違いましたか?たぶん、あなたは、より簡単にあなたのコードをモデル化し、データの「本質」の上に抽象化を作成することができること兆候だこと-などにかかわらず、デッキのカードの、あなたは本当にの表現を持っている必要があります[ HeartsSpadesDiamondsClubs]場合あなたはカードが赤/黒の場合にのみカード?
anotherdave

1
@ IS1_SOを使用すると、エラーコードの列挙があり、エラーをスローすることができnull_ptrます。これで、enumを介したエラーコードがあります。null_ptrエラーをチェックするコードは、enumを介してコードを検索します。そのため、5(たとえば)の値を持つ場合があります。次に、別のエラーコードを追加する必要があります。列挙型が変更されます(列挙型の上部に新しいものを追加するとします)の値null_ptrはnow 6です。これは問題ですか?のエラーコードを返し、6テストし6ます。すべてが論理的に一貫している限り、この変更が理論的なテストに違反していても、問題ありません。
Baldrickk

17

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

enum Parity {
    Even,
    Odd
}

Parity GetParity(int x) { ... }

あなたはしないでくださいその後、列挙型の検証書き込みテストParity定義を名がEvenしてOdd。このようなテストは、コードで既に述べられていることを繰り返すだけなので、意味がありません。同じことを2回言っても、それがより正確になるわけではありません。

あなたは行う検証する書き込みテストGetParityと言うが返されEven、0のためOdd1などのために。コードを繰り返すのではなく、実装とは無関係にコードの動作を検証しているため、これは重要です。内部のコードGetParityが完全に書き直された場合でも、テストは有効です。実際、単体テストの主な利点は、コードが期待どおりに機能することを保証することにより、コードを安全に書き直し、リファクタリングできることです。

ただし、enum 宣言で予想される名前が定義されていることを確認するテストがある場合、今後enumに変更を加えると、テストも変更する必要があります。これは、作業が2倍になるだけでなく、単体テストのメリットが失われることを意味します。コードの変更とテストを同時に行う必要がある場合、バグの導入に対する保護策はありません。


この回答に対処するために質問を更新しました。それが役立つかどうかを確認してください。
IS1_SO

@ IS1_SO:わかりました。混乱します。enum値を動的に生成していますか、それとも何が起こっていますか?
ジャックB

いいえ。この場合、値を表すために選択された言語構成体は列挙型です。しかし、私たちが知っているように、それは実装の詳細です。値を表すために配列、またはSet <>(Javaの場合)または分離トークンを含む文字列を選択するとどうなりますか?その場合は、含まれている値がビジネスにとって関心のあるものであることをテストするのが理にかなっています。それが私のポイントです。この説明は役に立ちますか?
IS1_SO

3
@ IS1_SO:関数から返された列挙インスタンスに特定の期待値があることをテストすることについて話しているのですか?はい、テストできるからです。列挙宣言自体をテストする必要はありません。
ジャックB

11

列挙型を変更するとコードが破損するリスクがある場合は、C#の[Flags]属性を持つものが適切です。2〜4(3)の値を追加すると、ビットではなく1と2になるためです。控えめなアイテム。

それは保護の層です。

すべての開発者が精通している実践の列挙コードを持つことを検討する必要があります。列挙型のテキスト表現に依存しないことは一般的ですが、これはシリアル化ガイドラインと競合する可能性があります。

私は人々が列挙型エントリの大文字を「修正」し、アルファベット順にソートするか、他の悪いコードを壊した他の論理的なグループ分けをすることを見てきました。


5
列挙の数値がどこにでも使用されている場合、たとえばデータベースに格納されている場合、並べ替え(最後の値の前の削除または挿入を含む)により、既存のレコードが無効になる可能性があります。
スタニウス

3
+1、この回答は過小評価されています。列挙型がシリアル化、外部ワードまたはビット単位の構成可能な情報との入力インターフェイスの一部である場合、システムの各バージョンで一貫性をテストする必要があります。少なくとも後方互換性が心配な場合は、通常は良いことです。
マチャド

11

いいえ、enumにすべての有効な値が含まれていること、および本質的にenumの宣言を繰り返していることを確認するテスト。あなたは、言語が無意味なテストである列挙構造を適切に実装することだけをテストしているでしょう。

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


3

コードは、enumの実際の値に関係なく正しく動作するはずです。その場合、単体テストは必要ありません。

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


1

一般に、enumにハードコードされた値のリストがあることを確認するだけでは、他の回答が言ったようにあまり価値がありません。

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

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

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


1

列挙型は、カスタム(できれば意味のある)名を持つ単純な有限型です。列挙型には値が1つしかない場合があります。voidこれには、値のみが含まれますnull(一部の言語ではこれを呼び出しunit、要素のないvoid列挙型の名前を使用します!)。これには、which とのような2つの値があります。、およびのように、3つあります。等々。boolfalsetruecolourChannelredgreenblue

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

たとえば、/ / resultを含むことは上記と同型です。たとえば、with 、with 、with 、with 、そしてどこでもそうする限り(プロデューサーとコンシューマー、パーサーとシリアライザー、データベースエントリー、ログファイルなど)を置き換えることができるからです。 )その後、プログラムに変更はありません。私たちが書いた「テスト」は、もうないにもかかわらず、合格します!winlosedrawcolourChannelcolourChannelresultredwingreenlosebluedrawcolourChannelcolourChannel

また、enumに複数の値が含まれる場合、それらの値をいつでも再配置して、同じ数の値を持つ新しい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が言うように、そのようなテストはプログラムではなく言語の実装をチェックします。これらのテストは決して良いアイデアではありません。言語の実装を信頼していなくても、テストを正しく実行することは信頼できないため、外部からテストする必要があります。

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

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

そのようなシナリオでは、列挙型は赤字です。あるシステムがこのようにする必要があり、別のシステムがそのようにする必要がある場合どうでしょうか。いくつのテストを書いても、両方にすることはできません!ここでの本当の犯人は入力/出力インターフェイスです。これは「解釈する整数が何であれ」ではなく、明確に定義されたフォーマットを生成/消費する必要があります。したがって、実際の解決策は、I / Oインターフェースをテストすることです。ユニットテストを使用して、期待される形式を解析/印刷していることを確認し、統合テストを使用して、形式が反対側で実際に受け入れられていることを確認します

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

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

1

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

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

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

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


0

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

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

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

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.