4

TDD を私の単純なプロジェクトに適用して学習しようとしています。詳細(および以前の質問)は次のとおりです。

TDD: テスト可能なクラスの作成に関するヘルプ

具体的には、PurchaseOrders のプライベート リスト (コンストラクターで渡される) を持つ PurchaseOrderCollection クラスがあり、PurchaseOrders にはブール値のプロパティ IsValid があります。PurchaseOrderCollection には、リスト内のいずれかの PurchaseOrders の IsValid が false の場合に true を返す HasErrors プロパティがあります。これが私がテストしたいロジックです。

[TestMethod]
public void Purchase_Order_Collection_Has_Errors_Is_True_If_Any_Purchase_Order_Has_Is_Valid_False()
{
    List<PurchaseOrder> orders = new List<PurchaseOrder>();

    orders.Add(new PurchaseOrder(--some values to generate IsValid false--));
    orders.Add(new PurchaseOrder(--some values to generate IsValid true--));

    PurchaseOrderCollection collection = new PurchaseOrderCollection(orders);

    Assert.IsTrue(collection.HasErrors);
}

これは私の前の質問と似ていますが、このテストは結合されすぎているという点で、テストに合格するために PurchaseOrder IsValid を false または true にするロジックを知る必要があります。クラス自体が問題ではないという点で、質問は異なります(imo)。

基本的に、IsValid が false または true である PurchaseOrder を、PurchaseOrder とは何かを知らなくても宣言できるようにしたいと考えています。

私の限られた TDD の知識から、これはスタブまたはモックを使用するものです。私の主な質問は、これは正しいですか? または、これには別の方法を使用する必要がありますか? それとも、私は完全に欠陥があり、このテストを書いて間違って考えているだけですか?

私が最初に考えたのは、ある種のモック フレームワークを使用して、常に true または false を返す PurchaseOrder を作成することでした。私が読んだことから、 IsValid virtual を宣言する必要があります。2 番目に考えたのは、PurchaseOrder のインターフェイスとして IPurchaseOrder を追加し、常に false または true を返す偽の PurchaseOrder を作成するように変更することでした。これらは両方とも有効なアイデアですか?

ありがとう!

4

7 に答える 7

6

スタブまたはモックを作成することで、正しい軌道に乗っています。モッキングフレームワークを使用することを好みます。

モック フレームワークを使用してどのように機能するかというと、 PurchaseOrder クラスをモックして、その実装を抽象化することです。次に、 IsValid が呼び出され、呼び出されたときにこの値を返すという期待を設定します。

C# 3.0 と .NET Framework 3.5 を使用している場合のMoqの使用例:

[TestMethod]
public void Purchase_Order_Collection_Has_Errors_Is_True_If_Any_Purchase_Order_Has_Is_Valid_False()
{    
    var mockFirstPurchaseOrder = new Mock<IPurchaseOrder>();
    var mockSecondPurchaseOrder = new Mock<IPurchaseOrder>();

    mockFirstPurchaseOrder.Expect(p => p.IsValid).Returns(false).AtMostOnce();
    mockSecondPurchaseOrder.Expect(p => p.IsValid).Returns(true).AtMostOnce();

    List<IPurchaseOrder> purchaseOrders = new List<IPurchaseOrder>();
    purchaseOrders.Add(mockFirstPurchaseOrder.Object);
    purchaseOrders.Add(mockSecondPurchaseOrder.Object);

    PurchaseOrderCollection collection = new PurchaseOrderCollection(orders);

    Assert.IsTrue(collection.HasErrors);
}

編集:
ここでは、PurchaseOrder のモックを作成するためにインターフェイスを使用しましたが、あなたも持っていません。IsValid を仮想としてマークし、 PurchaseOrder クラスをモックできます。どちらに進むべきかについての私の経験則は、最初に仮想を使用することです。インターフェースを作成するだけで、アーキテクチャ上の理由なしにオブジェクトをモックできるようにすることは、私にとってコードの匂いです。

于 2009-01-26T15:50:53.743 に答える
2

...このテストは、テストに合格するために PurchaseOrder IsValid を false または true にするロジックを知る必要があるという点で結合されすぎています。

実際には逆のことを主張します-有効性が購入注文内のブール値としてモデル化されていることをテストが知っているということは、テストが PurchaseOrder の実装についてあまりにも多く知っていることを意味します(実際には PurchaseOrderCollection のテストであると仮定します)。適切なテスト オブジェクトを作成するために現実世界の知識 (つまり、有効または無効になる実際の値) を使用することに問題はありません。最終的に、それは実際にあなたがテストしているものです (コレクションにばかげた値の注文書を発行した場合、エラーがあることを正しく教えてくれるでしょうか)。

一般に、私は、テスト以外の理由がない限り、PurchaseOrder などの「エンティティ」オブジェクトのインターフェイスを作成しないようにしています (たとえば、運用環境に複数の種類の PurchaseOrder があり、それをモデル化するにはインターフェイスが最適な方法です)。 )。

テストによって、製品コードをより適切に設計できることが明らかになったときは、素晴らしいことです。ただし、テストを可能にするためだけに製品コードを変更するのはあまり良くありません。

十分に書いていないかのように、ここに別の提案があります。これは、実際にこれを解決する方法です。

インターフェイスを持つ PurchaseOrderValidityChecker を作成します。isValid ブール値を設定するときにそれを使用します。ここで、与える回答を指定できる有効性チェッカーのテスト バージョンを作成します。(このソリューションでは、おそらく PurchaseOrderFactory または PurchaseOrder を作成するための同等のものが必要になることに注意してください。これにより、各注文書の作成時に PurchaseOrderValidityChecker への参照を与えることができます。)

于 2009-01-26T15:49:21.843 に答える
1

ここでいくつかのコンテキストが欠落している可能性がありますが、例のようにテストを「結合」する必要があるように思えます。そうしないと、実際には何もテストしていません(些細なIsValidプロパティを除く)。

注文書をモックしても何も得られない - 実際のクラスではなく、モックをテストしたことになる

スタブを使用する - 同じこと

必須ではないにしても、TDD を使用する場合のホワイトボックス テストは問題ありません

于 2009-01-26T17:11:54.297 に答える
1

最初に、 ではなくコレクションをテストしていることを思い出してくださいPurchaseOrder。複雑さによって異なりますPurchaseOrder。明らかな振る舞いをする単純なエンティティであれば、インスタンスを作成するだけで十分でしょう。より複雑な場合は、説明したようにインターフェイスを抽出するのが理にかなっています。

次に発生する問題は、そのインターフェイスにあるものです。コレクション内のオブジェクトが実行する必要がある役割は何ですか? それらが有効かどうかを知るだけでよい場合もあります。その場合IValidatable、コード内の依存関係を抽出して絞り込むことができます。この場合、何が正しいのかわかりませんが、インターフェイスを使用して、より焦点を絞ったコードにプッシュできることがよくあります。

于 2009-09-06T20:01:20.013 に答える
1

私は最近、テストについてやや似たような質問をしました。これを忘れないでください: 必要な最も単純なことを行い、必要に応じてリファクタリングしてください。私は個人的には全体像を念頭に置いておくようにしていますが、自分のソリューションを過度に設計する衝動にも抵抗しています. 1 つが有効で 1 つが無効である 2 つの PurchaseOrder フィールドをテスト クラスに追加できます。これらのフィールドを使用して、 PurchaseOrderCollection をテストしたい状態にします。最終的にはモックの方法を学ぶ必要がありますが、この場合、通常のハンマーで問題が解決される場合、大ハンマーは必要ありません。必要な状態にある具体的な PurchaseOrder の代わりにモック PurchaseOrder を使用しても、値は得られません。

最も重要なことは、PurchaseOrderCollection の状態をテストするだけでなく、PurchaseOrderCollection の動作をテストすることで、より多くのことを得ることができるということです。PurchaseOrderCollection をさまざまな状態にできることをテストで確認したら、より重要なテストは動作テストです。適切と思われる方法 (目的の状態で具体的なクラスをモックまたは新しく作成する) によって、発注書コレクションを有効な状態と無効な状態の両方に配置し、その PurchaseOrderCollection だけでなく、 PurchaseOrderCollection の各状態のロジックが適切に実行されることをテストします。単に有効/無効な状態です。

PurchaseOrderCollection は特殊なコレクションであるため、常に別のクラスに依存します。IPurchaseOrder に IsValid プロパティがあることを知っていることは、具体的な PurchaseOrder に IsValid プロパティがあることを知っていることと何ら変わりはありません。システムに複数の種類の PurchaseOrder があると信じるに足る理由がない限り、具体的な PurchaseOrder など、機能する最も単純なものに固執します。その時点で、 PurchaseOrder インターフェースの方が理にかなっています。

于 2009-01-26T16:37:19.547 に答える
0

私はユニットテストの専門家ではありませんが、これが過去に行ったことです。有効/無効のPurchaseOderクラスがある場合は、それらの単体テストもあり、実際に検証されるかどうかを確認します。これらのメソッドを呼び出して有効および無効なPurchaseOrderオブジェクトを生成し、それをコレクションに追加してみませんか?

于 2009-01-26T15:36:27.377 に答える
0

これらは両方とも有効なアイデアですか?

はい。

有効な購入注文と無効な購入注文の両方を返すオブジェクト マザーを作成することもできます。

于 2009-01-26T16:29:02.120 に答える