10

私は、単体テストに重点を置いて作成されたかなり最新のプロジェクトを見ていました。「オブジェクト指向プログラミングのすべての問題は、新しい間接層を導入することで解決できる」という古い格言に従って、このプロジェクトは複数層の間接層を誇示していました。副作用は、かなりの量のコードが次のようになることでした。

public bool IsOverdraft)
{
    balanceProvider.IsOverdraft();
}

現在、単体テストと高いコード カバレッジの維持に重点が置かれているため、コードのすべての部分にそれに対して書かれた単体テストがありました。したがって、この小さなメソッドには 3 つの単体テストが存在します。それらはチェックします:

  1. balanceProvider.IsOverdraft() が true を返す場合、IsOverdraft は true を返す必要があります
  2. balanceProvider.IsOverdraft() が false を返す場合、IsOverdraft は false を返す必要があります
  3. balanceProvider が例外をスローした場合、 IsOverdraft は同じ例外を再スローする必要があります

さらに悪いことに、使用されているモッキング フレームワーク (NMock2) は、次のようにメソッド名を文字列リテラルとして受け入れました。

NMock2.Expect.Once.On(mockBalanceProvider)
    .Method("IsOverdraft")
    .Will(NMock2.Return.Value(false));

それは明らかに「赤、緑、リファクタリング」ルールを「赤、緑、リファクタリング、テストで名前を変更、テストで名前を変更、テストで名前を変更」にしました。Moq のような別のモック フレームワークを使用すると、リファクタリングに役立ちますが、既存のすべての単体テストを一掃する必要があります。

この状況を処理する理想的な方法は何ですか?

A) レイヤーのレベルを小さくして、これらの転送呼び出しが発生しないようにします。

B) ビジネス ロジックが含まれていないため、これらの転送方法をテストしないでください。カバレッジのために、それらすべてを ExcludeFromCodeCoverage 属性でマークしました。

C) 戻り値や例外などをチェックせずに、適切なメソッドが呼び出されたかどうかのみをテストします。

D) やめて、テストを書き続けてください ;)

4

5 に答える 5

5

BまたはCのいずれかです。これは、そのような一般的な要件の問題です(「すべてのメソッドに単体テストが必要であり、コードのすべての行をカバーする必要があります」)-時には、それらが提供する利点はコストに見合うものではありません。それが思いついたものである場合は、このアプローチを再考することをお勧めします. 「95% のコード カバレッジが必要」という言葉は紙の上では魅力的かもしれませんが、実際には、あなたが抱えているような問題がすぐに発生します。

また、テストしているコードは、私が自明なコードと呼ぶものです。3 つのテストを行うのは、やり過ぎの可能性が高いです。その 1 行のコードに対して、さらに 40 行ほど維持する必要があります。あなたのソフトウェアがミッション クリティカルでない限り (高いカバレッジ要件を説明する可能性があります)、それらのテストはスキップします。

このトピックに関する(IMHO)最も実用的なアドバイスの 1 つは、 Kent Beck によってこのサイトで少し前に提供されました

于 2013-01-20T00:06:39.500 に答える
4

正直なところ、コードを役に立つ方法で文書化するためだけにテストを書くべきだと思います。コードカバレッジのためだけにテストを書くべきではありません。(コード カバレッジは、重要な単体テスト ケースを忘れていないか、または実際にデッド コードがどこかにあるかどうかを判断できるように、カバーされていないものを把握するための優れたツールです)。

私がテストを書いたとしても、そのテストが単に実装の「重複」で終わるか、それよりも悪い場合...実際の実装よりもテストを理解するのが難しい場合....そのようなテストは実際には存在すべきではありません。そのようなテストを読むことに誰も興味がありません。テストには実装の詳細を含めないでください。テストは、 「どのように」行われるかではなく、 「何を」行うべきかに関するものです。質問に「TDD」というタグを付けたので、TDD は設計手法であると付け加えておきます。したがって、実装しようとしているものの設計が何であるかを事前に 100% 確実に知っている場合、TDD を使用して単体テストを作成する意味はありません (しかし、私はすべての場合において、そのコードをカバーする高レベルの受け入れテストを常に行います)。あなたの例のように、設計するものが本当に単純な場合によく起こります。TDD はテストやコード カバレッジに関するものではなく、実際にはコードの設計とコードの文書化を支援するものです。単純な/明白なものを設計/文書化するために設計ツールや文書化ツールを使用する意味はありません。

あなたの例では、テストよりも実装を直接読むことで何が起こっているのかを理解するのがはるかに簡単です。このテストは、文書化に関して何の価値も追加しません。なので、喜んで消します。

その上、そのようなテストは実装に密接に結合されているため、非常に脆弱です。実装を変更したいときはいつでもそれらが壊れるからです。

私が提案したいのは、そのようなテストを作成するのではなく、内部の作業について何も知らずにこれらのレイヤーを実行する、より高いレベルのコンポーネント テストまたは高速な統合テスト/受け入れテストを作成することです。

于 2013-01-20T00:44:55.770 に答える
1

単体テストで心に留めておくべき最も重要なことの 1 つは、コードが現在どのように実装されているかは必ずしも重要ではなく、直接または間接的にテストされたコードが将来変更されたときに何が起こるかということです。

現在、これらのメソッドを無視し、それらがアプリケーションの操作にとって重要である場合、誰かが将来のある時点で新しい balanceProvider を実装することを決定するか、リダイレクトがもはや意味をなさないと判断する場合、おそらく障害点が発生します。

したがって、これが私のアプリケーションである場合、最初に転送のみの呼び出しを最小限に減らし (コードの複雑さを軽減する)、次にメソッド名の文字列値に依存しないモック フレームワークを導入します。

于 2013-01-20T00:20:13.523 に答える
1

ここでの議論に追加することがいくつかあります。

すぐに、段階的に、より優れたモッキング フレームワークに切り替えます。 約 3 年前に RhinoMock から Moq に切り替えました。すべての新しいテストは Moq を使用しており、多くの場合、テスト クラスを変更するときにそれを切り替えます。しかし、あまり変更されていない、または巨大なテストケースがあるコードの領域は、まだ RhinoMock を使用しており、それで問題ありません。切り替えを行った結果、日々使用するコードが大幅に改善されました。すべてのテスト変更は、この段階的な方法で発生する可能性があります。

あなたはあまりにも多くのテストを書いています。TDD で留意すべき重要な点は、赤いテストを満たすためのコードのみを記述し、記述されていないコードを指定するためのテストのみを記述する必要があるということです。したがって、あなたの例では、3 つのテストは過剰です。なぜなら、そのすべての製品コードを強制的に記述するには、最大で 2 つのテストが必要だからです。 例外テストでは、新しいコードを作成する必要がないため、作成する必要はありません。 私はおそらくこのテストだけを書くでしょう:

[Test]
public void IsOverdraftDelegatesToBalanceProvider()
{
    var result = RandomBool();
    providerMock.Setup(p=>p.IsOverdraft()).Returns(result);
    Assert.That(myObject.IsOverDraft(), Is.EqualTo(result);
}

無用な間接層を作成しないでください。 ほとんどの場合、間接化が必要かどうかは単体テストでわかります。ほとんどの間接的なニーズは、依存関係の逆転の原則、つまり「具体化ではなく抽象化への結合」によって解決できます。他の理由でいくつかのレイヤーが必要です (私は WCF ServiceContract 実装を薄いパススルー レイヤーにしています。また、そのパススルーもテストしていません)。役に立たない間接的なレイヤーが見つかった場合は、1) 本当に役に立たないことを確認してから、2) 削除します。コードが乱雑になると、時間の経過とともに大きなコストがかかります。Resharper は、これをとてつもなく簡単かつ安全にします。

また、意味のある委任または委任のシナリオを取り除くことはできませんが、テストする必要がある場合、このようなものを使用すると、はるかに簡単になります。

于 2013-01-21T03:26:46.340 に答える
0

D) やめて、これらのテストを書き続けてください ;) そして、NMock を MOQ に置き換えることができるかどうかを試してみてください。

必要ではないように見えるかもしれませんし、今は単なる委譲ですが、テストでは、正しいパラメーターで正しいメソッドを呼び出していること、およびメソッド自体が値を返す前に奇妙なことをしていないことをテストしています。したがって、それらをテストでカバーすることをお勧めします。しかし、それを簡単にするために、MOQ または同様のフレームワークを使用すると、リファクタリングがはるかに簡単になります。

于 2013-01-19T23:59:40.673 に答える