ゲッターとセッターのためにユニットテストを書くべきですか?


141

私たちはゲッターとセッターのテストを書くことになっていますか、それともやりすぎですか?


そうは思いません。ゲッター/セッターのテストケースを書くべきではありません。
ハリージョイ

1
私はあなたがJavaを意味すると思いますか?これはJavaにとって特に深刻な問題であり、より最近の言語ではそれほど問題ではありません。
skaffman

@skaffmanどの現代言語にプロパティがないのですか?もちろん、Javaのような言語では、それらを完全なメソッド本体にする必要がありますが、C#と論理的に異なるわけではありません。
クラウスヨルゲンセン、2011年

2
@クラウス:彼は特性を言ったのではなく、ゲッターとセッターを言った。Javaではそれらを手動で記述し、他の言語ではより良いサポートを得ます。
skaffman

2
なぜゲッターとセッターがあるのかと尋ねる人もいるかもしれません。
Raedwald、2014

回答:


180

私はノーと言うでしょう。

@ 100%コードカバレッジを目指すべきだと言いますが、私の意見では危険な注意散漫です。カバレッジが100%の単体テストを作成できますが、まったく何もテストしません。

ユニットテストは、表現力豊かで意味のある方法でコードの動作をテストするためのものであり、ゲッター/セッターは目的を達成するための手段にすぎません。テストでゲッター/セッターを使用して「実際の」機能をテストするという目標を達成すれば、それで十分です。

一方、ゲッターとセッターが単に取得して設定するだけではない場合(つまり、適切に複雑なメソッドである場合)、はい、テストする必要があります。ただし、ゲッターを設定するためだけにユニットテストケースを作成しないでください。これは時間の無駄です。


7
100%のコードカバレッジを目指すのはばかげています。公開されていないコードや複雑でないコードをカバーすることになります。そのようなものは自動生成に任せるのが最善ですが、自動生成されたテストでさえ無意味です。統合テストなどの重要なテストに焦点を移すことをお勧めします。
クラウスヨルゲンセン、2011年

7
@クラウス:統合テストに焦点を当てることについてのビットを除いて、私は同意します。単体テストは優れた設計に不可欠であり、統合テストを補完します。
skaffman

5
テストスイート全体を実行したときの100%のコードカバレッジは良い目標だと思います。プロパティゲッターセッターおよびその他のコードは、より大きなテストの一部として実行されます。カバーされる最後の部分はおそらくエラー処理です。エラー処理が単体テストでカバーされていない場合は、カバーされません。これまでに実行されたことのないコードを含む製品を出荷しますか?
アンデルスアベル

私はこのアドバイスに反対する傾向があります。はい、ゲッターとセッターは単純ですが、驚くほど簡単に失敗します。数行の簡単なテストで、それらが機能し、機能し続けていることがわかります。
Charles

「丸められた」POJOを作成するためだけに作成した不要なセッター(またはツール)をテストしている可能性があります。インスタンスごとにGson.fromJsonを使用してPOJOSを「膨らませる」ことができます(セッターは不要)。この場合、私の選択は未使用のセッターを削除することです。
Alberto Gaona 2018

36

Roy Osheroveは、著書「The Art Of Unit Testing」で次のように述べています。

プロパティ(Javaのゲッター/セッター)は、通常はロジックを含まず、テストを必要としないコードの良い例です。ただし、注意してください。プロパティ内にチェックを追加したら、ロジックがテストされていることを確認する必要があります。


1
それらをテストするのにかかる時間と動作が追加される可能性を考えると、なぜそうでないのかわかりません。もし押されたら彼は「まあ、どうしてだろう」と答えるかもしれません。
そり

1
重要な初期の本はしばしば悪いアドバイスを持っています。ゲッターやセッターをすばやく投げて、カットアンドペーストしたり、忘れたりしてミスを犯すケースをたくさん見ました。またはthis->またはそのようなもの。テストは簡単で、必ずテストする必要があります。
Charles

35

TDDで鳴り響くYES


:この回答は、悪いアドバイスになる可能性はありますが、賛成票を獲得し続けます。その理由を理解するには、以下の妹をご覧ください。


議論の余地はありますが、この質問に「いいえ」と答えた人は誰でもTDDの基本的な概念を欠いていると私は主張します。

私にとって、TDDをフォローしていれば、答えはイエスです。そうでない場合は、もっともらしい答えはありません。

TDDのDDD

TDDは、主な利点があるとよく言われます。

  • 防衛
    • 確保コードは変更されることがありますが、その動作をしませ
    • これにより、これまでになく重要なリファクタリングの手法が可能になります。
    • このTDDを獲得するかどうか。
  • 設計
    • あなた何かが何をすべきか、それが実装する前にどのように振る舞うかを指定しますにそれを。
    • これは多くの場合、より多くの情報に基づいた実装決定を意味します。
  • ドキュメンテーション
    • テストスイートは仕様として機能する必要があります(要件)ドキュメントます。
    • そのような目的でテストを使用することは、ドキュメントと実装が常に一貫した状態にあることを意味します-1つの変更は他の変更を意味します。要件と設計を別のワードドキュメントに保存する場合と比較してください。

責任を実装から分離する

プログラマーとして、属性を重要なものと考え、ゲッターとセッターをある種のオーバーヘッドと考えるのはひどく魅力的です。

ただし、属性は実装の詳細であり、セッターおよびゲッターは実際にプログラムを機能させる契約上のインターフェースです。

オブジェクトが次のことを行うことを綴ることははるかに重要です。

クライアントが状態を変更できるようにする

そして

クライアントがその状態を照会できるようにする

次に、この状態が実際に格納される方法(属性が最も一般的ですが、唯一の方法ではありません)。

次のようなテスト

(The Painter class) should store the provided colour

TDD のドキュメント部分にとって重要です。

最終的な実装が取るに足らないこと(属性)であり、防御上の利点がないことは、テストを作成するときにはわかりません。

往復エンジニアリングの欠如...

システム開発の世界で重要な問題の一つは、の欠如である ラウンドトリップエンジニアリング1 -システムの開発プロセスは(ドキュメント、コード)はしばしば矛盾しているアーティファクトそのとりとめのないサブプロセスに細分化されます。

1ブロディ、マイケルL.「ジョンミロポーロス:概念モデリングの種を縫う」。概念モデリング:基礎とアプリケーション。スプリンガーベルリンハイデルベルク、2009年。1-9。

...そしてTDDがそれを解決する方法

システムとそのコードの仕様が常に一貫していることを保証するのは、TDD のドキュメント部分です。

最初に設計し、後で実装する

TDD内では、まず不合格の受け入れテストを記述し、次に合格するコードを記述します。

上位レベルのBDD内では、まずシナリオを記述してから、それを通過させます。

セッターとゲッターを除外する必要があるのはなぜですか?

理論的には、TDD内で1人がテストを記述し、もう1人が合格するコードを実装することは完全に可能です。

だから自問してください:

クラスのテストを書いている人がゲッターとセッターに言及すべきかどうか。

ゲッターとセッターはクラスへのパブリックインターフェイスであるため、答えは明らかにyesです。そうでない場合、オブジェクトの状態を設定または照会する方法はありません。ただし、これを行う方法は必ずしも各メソッドを個別にテストすることではありません。詳細については、他の回答を参照してください。

明らかに、最初にコードを書く場合、答えはそれほど明確ではないかもしれません。


2
それはコインの片面にすぎません。テストのためだけにテストするのではなく、できることを考えてください。ケントベックが言ったように、実際のテストではなく、実際のコードに対して報酬を得ます。
Georgii Oleinikov

@GeorgiiOleinikov下記の私のその他の回答を読んでください。それはあなたの見解とほぼ一致しています。
イザキ

21

tl; dr: はいそうすべきです。OpenPojoを使用すると、それは簡単です。

  1. ゲッターとセッターで検証を行う必要があるので、それをテストする必要があります。たとえばsetMom(Person p)、自分より若い人を母親にすることはできません。

  2. 現時点でそれを実行していない場合でも、将来的にそうなる可能性は高いので、これは回帰分析に適しています。あなたが母親をnullあなたに設定することを許可したい場合は、誰かが後でそれを変更した場合のテストをする必要があります、これはあなたの仮定を強化します。

  3. 一般的なバグはvoid setFoo( Object foo ){ foo = foo; }、どこにあるべきかvoid setFoo( Object foo ){ this.foo = foo; }です。(最初の場合fooに書き込まれていることであるパラメータ ではないfoo上フィールドオブジェクト)。

  4. 配列またはコレクションを返す場合、ゲッターが戻る前にセッターに渡されたデータの防御コピーを実行するかどうかをテストする必要があります。

  5. そうでなければ、もしあなたが最も基本的なセッター/ゲッターを持っているなら、それらを単体テストすると、オブジェクトごとにせいぜい約10分を追加することになるので、何が損失でしょうか?動作を追加すると、すでにスケルトンテストがあり、この回帰テストは無料で受けられます。Javaを使用している場合、OpenPojoがあるので言い訳はありません。既存のルールセットを有効にしてから、プロジェクト全体をスキャンして、コード内で一貫して適用されることを確認できます。

彼らのから:

final PojoValidator pojoValidator = new PojoValidator();

//create rules
pojoValidator.addRule( new NoPublicFieldsRule  () );
pojoValidator.addRule( new NoPrimitivesRule    () );
pojoValidator.addRule( new GetterMustExistRule () );
pojoValidator.addRule( new SetterMustExistRule () );

//create testers
pojoValidator.addTester( new DefaultValuesNullTester () );
pojoValidator.addTester( new SetterTester            () );
pojoValidator.addTester( new GetterTester            () );

//test all the classes
for(  PojoClass  pojoClass :  PojoClassFactory.getPojoClasses( "net.initech.app", new FilterPackageInfo() )  )
    pojoValidator.runValidation( pojoClass );

11
私はこれが古いものであることを知っていますが、ゲッターとセッターで検証を行う必要がありますか?私はsetMomが言うとおりにすべきだという印象を受けました。検証している場合は、validateAndSetMomにすべきではありませんか?または、単純なオブジェクトよりも検証コードがELSEWHERE以外に存在するべきではありませんか?ここで何が欠けていますか?
ndtreviv 2013年

14
はい、常に入力を検証する必要があります。そうでない場合は、なぜパブリック変数を使用しないのですか?同じことになります。変数とセッターを使用することの全体的な利点は、年齢が負の数になるなど、オブジェクトが無効にならないことを保証できることです。オブジェクトでこれを行わない場合は、他の場所(「サービス」の神オブジェクトなど)で行いますが、その時点では実際にはOOPではありません。
そり2013年

1
まあ、それはおそらく私の週のハイライトです。ありがとうございました。
そり

19

はい、しかし常に孤立しているわけではありません

詳しく説明します:

単体テストとは何ですか?

レガシーコード1を効果的に使用することから:

ユニットテストという用語には、ソフトウェア開発において長い歴史があります。単体テストのほとんどの概念に共通するのは、それらがソフトウェアの個々のコンポーネントを分離したテストであるという考えです。コンポーネントとは何ですか?定義はさまざまですが、単体テストでは通常、システムの最もアトミックな動作ユニットに関心があります。手続き型コードでは、単位は多くの場合関数です。オブジェクト指向コードでは、単位はクラスです。

ゲッターとセッターを見つけるOOPでは、ユニットはクラスであり、必ずしも個々のメソッドではないことに注意してください

良いテストとは何ですか?

すべての要件とテストは、Hoareロジックの形式に従います。

{P} C {Q}

どこ:

  • {P}前提条件は、(されて与えられました
  • Cトリガー条件です(いつ
  • {Q}事後条件です(then

次に格言が来る:

実装ではなくテスト動作

つまりC、事後条件がどのように達成されるかをテストするのではなく、それ{Q}がの結果であることを確認する必要がありCます。

OOPに関してCは、クラスです。したがって、内部効果はテストせず、外部効果のみをテストしてください。

Beanゲッターとセッターを分離してテストしないのはなぜですか

ゲッターとセッターにはいくつかのロジックが含まれる場合がありますが、このロジックが外部に影響しない限り、Beanアクセサーになります2)のテストではオブジェクトの内部を調べる必要があり、それによってカプセル化に違反するだけでなく、実装のテストも行われます。

そのため、Beanのゲッターとセッターを分離してテストしないでください。これは悪いです:

Describe 'LineItem class'

    Describe 'setVAT()'

        it 'should store the VAT rate'

            lineItem = new LineItem()
            lineItem.setVAT( 0.5 )
            expect( lineItem.vat ).toBe( 0.5 )

setVAT例外がスローされる場合でも、外部効果あるため、対応するテストが適切です。

ゲッターとセッターをどのようにテストすべきですか?

オブジェクトの内部状態を変更しても外部に影響がない場合、そのような影響が後で発生しても、事実上、オブジェクトの内部状態を変更しても意味がありません。

したがって、setterおよびgetterのテストは、内部メソッドではなく、これらのメソッドの外部効果に関係する必要があります。

例えば:

Describe 'LineItem class'

    Describe 'getGross()'

        it 'should return the net time the VAT'

            lineItem = new LineItem()
            lineItem.setNet( 100 )
            lineItem.setVAT( 0.5 )
            expect( lineItem.getGross() ).toBe( 150 )

あなたは自分で考えるかもしれません:

少し待ってください。getGross()ここではテストしていません setVAT()

しかし、setVAT()誤動作した場合、そのテストはすべて同じように失敗します。

1 Feathers、M.、2004。レガシーコードを効果的に使用。プレンティスホールプロフェッショナル。

2マーティン、RC、2009年。クリーンなコード:アジャイルソフトウェアの職人技のハンドブック。ピアソン教育。


13

プロパティには正当な理由がありますが、プロパティを介してメンバーの状態を公開することは悪いデザインであるという一般的なオブジェクト指向の設計信念があります。オープンクローズドプリンシプルに関するRobert Martinの記事 Propertiesがカップリングを促進し、したがって、変更からクラスを閉じる機能を制限することを述べてこれを拡張します。プロパティを変更すると、クラスのすべてのコンシューマーも変更する必要があります。メンバー変数を公開することは必ずしも悪いデザインではなく、単に悪いスタイルかもしれないと彼は認めています。ただし、プロパティが読み取り専用の場合は、悪用や副作用の可能性が低くなります。

単体テストに提供できる最善のアプローチ(これは奇妙に思えるかもしれません)は、できるだけ多くのプロパティを保護または内部にすることです。これにより、ゲッターとセッターの愚かなテストを書くことを妨げながら、結合を防ぎます。

入力フィールドにバインドされているViewModelプロパティなど、読み取り/書き込みプロパティを使用する明確な理由があります。

より実際的には、単体テストはパブリックメソッドを介して機能を駆動する必要があります。テストしているコードがたまたまこれらのプロパティを使用する場合、無料でコードカバレッジを取得できます。これらのプロパティがコードカバレッジによって強調表示されないことが判明した場合、次のような非常に強力な可能性があります。

  1. プロパティを間接的に使用するテストがありません
  2. プロパティは未使用です

ゲッターとセッターのテストを作成すると、誤ったカバレッジの感覚が得られ、プロパティが実際に機能動作で使用されているかどうかを判断できなくなります。


12

ゲッターやセッターの循環的複雑度が1(通常は1)である場合、答えは「いいえ」であり、そうすべきではありません。

したがって、100%のコードカバレッジを必要とするSLAがない限り、気にせず、ソフトウェアの重要な側面のテストに集中してください。

PSプロパティが同じように見えるC#のような言語でも、ゲッターとセッターを区別することを忘れないでください。セッターの複雑さはゲッターよりも高くなる可能性があるため、単体テストを検証します。


4
ゲッターでの防御的なコピーをテストする必要があります
スレッド

8

滑稽でありながら賢明な見方証言の道

「今日できるテストを書いてください」

あなたが経験豊富なテスターであり、これが小さなプロジェクトである場合、ゲッター/セッターのテストはやり過ぎかもしれません。ただし、ユニットテストの方法を学び始めたばかりの場合、またはこれらのゲッター/セッターにロジック(@ArtBのsetMom()例など)が含まれている場合は、テストを作成することをお勧めします。


1
あなたのリンクは実際に次の場所を指す必要があります:Way of Testivus
JJS

2

これは実際、私のチームと私との間の最近のトピックです。80%のコードカバレッジを目指します。私のチームは、getterとsetterは自動実装されており、コンパイラーが舞台裏でいくつかの基本的なコードを生成していると主張しています。この場合、生成されたコードが邪魔にならないことを考えると、コンパイラーが作成したコードをテストすることは実際には意味がありません。非同期メソッドについてもこのディスカッションを行いました。その場合、コンパイラはバックグラウンドで大量のコードを生成します。これは別のケースであり、テストを行います。長い答えは短く、あなたのチームと一緒に持ってきて、テストする価値があるかどうか自分で決めてください。

また、私たちのようにコードカバレッジレポートを使用している場合は、[ExcludeFromCodeCoverage]属性を追加することができます。私たちの解決策は、ゲッターとセッターを使用するプロパティを持つモデル、またはプロパティ自体にこれを使用することです。これにより、コードカバレッジパーセンテージの計算に使用していると想定して、コードカバレッジレポートの実行時に合計コードカバレッジ%に影響を与えません。ハッピーテスト!


2

私の意見では、コードカバレッジは、カバーする必要のある機能を見逃したかどうかを確認するための良い方法です。

カバレッジをきれいに着色して手動で検査すると、プレーンゲッターとセッターはテストする必要がないと主張できます(私は常にテストします)。

プロジェクトのコードカバレッジパーセンテージのみをチェックする場合、80%などのテストカバレッジパーセンテージは意味がありません。すべての論理的でない部分をテストして、いくつかの重要な部分を忘れることができます。この場合、100%だけが、すべての重要なコード(およびすべての非論理コードも同様)をテストしたことを意味します。99.9%になるとすぐに、何かを忘れたことがわかります。

ちなみに、コードカバレッジは、クラスを完全に(ユニット)テストしたかどうかを確認する最終チェックです。ただし、100%のコードカバレッジは、必ずしもクラスのすべての機能を実際にテストしたことを意味するわけではありません。したがって、ユニットテストは常にクラスのロジックに従って実装する必要があります。最後に、カバレッジを実行して、何かを忘れたかどうかを確認します。あなたが正しくそれをしたとき、あなたは100%最初にヒットしました。

もう1つ:最近オランダの大手銀行で働いているときに、ソナーが100%コードカバレッジを示していることに気付きました。しかし、私は何かが欠けているのを知っていました。ファイルごとのコードカバレッジパーセンテージを調べると、ファイルのパーセンテージが低いことがわかりました。コードベース全体のパーセンテージは、1つのファイルではパーセンテージが99.9%と表示されないほど大きかった。だからあなたはこれに気をつけたいかもしれません...


1

JUnitコード自体で達成されたカバレッジを少し分析しました

カバーされていないコードのカテゴリの1つは、「テストが簡単すぎる」です。これには、JUnitの開発者が行わない単純なゲッターとセッターが含まれますテストしてます。

一方、JUnitには、テストでカバーされていない3行を超える(非推奨ではない)メソッドはありません。


1

私は言うでしょう:ゲッター/セッターメソッドでのエラーエラーは静かにこっそり侵入し、いくつかの醜いバグを引き起こす可能性があります。

私はこれと他のいくつかのテストを簡単にするためにlibを書きました。JUnitテストで記述しなければならないのはこれだけです。

        assertTrue(executor.execute(Example.class, Arrays.asList( new DefensiveCopyingCheck(),
            new EmptyCollectionCheck(), new GetterIsSetterCheck(),
            new HashcodeAndEqualsCheck(), new PublicVariableCheck())));

-> https://github.com/Mixermachine/base-test


0

はい、特に取得するアイテムが抽象クラスからサブクラス化されたクラスのオブジェクトである場合。IDEは、特定のプロパティが初期化されていないことを警告する場合と警告しない場合があります。

そして、明らかに無関係ないくつかのテストが NullPointerExceptionで、最初にgettableプロパティが実際に存在しないことを理解するにはしばらく時間がかかります。

それでも、本番環境で問題を発見するほど悪いことはありません。

すべての抽象クラスにコンストラクタがあることを確認することをお勧めします。そうでない場合、ゲッターのテストはそこでの問題を警告するかもしれません。

プリミティブのゲッターとセッターに関しては、問題は次のようになるでしょう:私は自分のプログラムをテストしていますか、それともJVMまたはCLRをテストしていますか?一般的に言って、JVMをテストする必要はありません。

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