3

私は単体テストに本当に慣れていないので、調査に膨大な時間を費やしましたが、自分の場合に適切な方法を見つけることができません。

私のコード ベースは巨大で (約 3 年間の作業)、非常に結合されており、残念ながらテストが難しく、ユニット テストが行​​われたことはありません。

たとえば、コレクション クラスProductCollection、より具体的にはそのクラスをテストしようとするbool MoveElementAtIndex(Product productToMove, int newIndex)と、次の問題が発生します。

  • まず、これを初期化する必要がありますnew ProductCollection()
  • コンストラクターは、別の手作りのクラスを初期化します: new KeyedList<ID, Product>. 私はテストしていないので、これはこのコンストラクター内で呼び出されるべきではないと思いますKeyedList
  • 次に、これに 3 つの製品を追加しようとしていProductCollectionます。
  • 次に、これら 3 を最初に作成しますnew Product()
  • しかし、Productクラスのコンストラクターはいくつかのことを行います
  • 新しく作成された製品の一意の ID を計算します: this.ID = IDUtils.ComputeNewIDBasedOnTheMoonPhase(). 私の範囲ではないので、これもテストすべきではないと思います。このレベルの深さでそのような呼び出しを回避するにはどうすればよいですか?
  • 同じ Product コンストラクターが、この製品にいくつかの既定のプロパティを割り当てます: this.Properties = new ProductProperties(folderPathToDefaultProperties). FieldCollection.MoveElementAtIndexこれは私の単純なテストから呼び出されるべきではありませんよね?
  • ようやく製品オブジェクトを手に入れ、コレクションに追加しようとしているとしましょう。
  • ただし、ProductCollection.Add(MyProduct)基礎にKeyedList既に製品が含まれているかどうかを確認します。これは、私のテストとは関係なく、避けるべきビジネス ロジックでもあります。問題はどのようにですか?
  • また、このAddメソッドでは、いくつかのイベントが発生し、いくつかのこと (たとえば、新しい製品がコレクションに追加されたこと) についてシステムに通知されます。これらもまったく解雇されるべきではないと思います。
  • そして最後に、製品を追加したら、必要な SUT を呼び出します: move elements メソッドです。
  • しかし、このメソッドには、私のテストの範囲外になる可能性のあるロジックもあります。基になるKeyedListフィールドに実際にこれらのフィールドが含まれていることを確認し、その移動ロジックに対して を呼び出しKeyedList.Remove()、のようなイベントを発生させます。KeyedList.Insert()CollectionModified

この単体テストを適切に行う方法、基になるオブジェクトが呼び出されないようにする方法を説明していただければ幸いです。

Microsoft の Moles フレームワーク (VS2010) について考えています。これは絶対に選択肢ではないため、すべてをリファクタリングする必要がないという印象があるからです。しかし、すでに試してみても、まだ適切な使用方法が見つかりません。

また、この具体的な例は私の状況で多くの人を助けるという印象を持っています.

何かご意見は?

4

4 に答える 4

4

コードは単体テストを念頭に置いて設計されていないため、単体テストを行うのは非常に困難です。新しいコードを適切に設計して単体テストし、最も重要なものをリファクタリングして、単体テストできるようにすることをお勧めします。

例:

しかし、Productクラスのコンストラクターはいくつかのことを行います。新しく作成された製品の一意のIDを計算します:this.ID = IDUtils.ComputeNewIDBasedOnTheMoonPhase()。私のスコープではないので、これもテストすべきではないと思います。このレベルの深さでこのような呼び出しを回避するにはどうすればよいですか?

これを修正するには、インターフェイスIUtilsをProductコンストラクターに渡す必要があります。Productクラスをテストするために、設定値を返すIUtilsのモックを作成できます。ProductPropertiesでも同じことができます。

また、このAddメソッドでは、いくつかのイベントが発生し、システムにいくつかのことを通知します(たとえば、新しい製品がコレクションに追加されたことを通知します)。これらもまた、私が思うにまったく解雇されるべきではありません。

これは設計に帰着します。単体テスト中は、オブザーバーパターンを使用し、オブザーバーを使用しないことができます。

于 2013-01-12T15:53:54.023 に答える
2

これは一般的な問題であり、オブジェクトを偽造するためのフレームワークがある理由です。スタブまたはモックを使用すると、他の多くのクラスやサービスをインスタンス化することなく、クラスの周囲にテストハーネスを作成できます。そのようなフレームワークはたくさんあります。これは、3つの一般的なフレームワークの非常に便利なブログです。 http://www.richard-banks.org/2010/07/mocking-comparison-part-1-basics.html

私は自分自身のユニットテストにかなり慣れておらず、RoyOsheroveによるTheArt ofUnitTestingという本が非常に役に立ちました。Royにはブログがあり、これらはユニットテストに関する彼の投稿の一部ですhttp://osherove.com/display/Search?moduleId=10002929&searchQuery=Unit+Testing

考慮しなければならないもう1つのことは、クラスがどれほど緊密に結合されているかです。それを行うのはそれほど簡単ではないかもしれませんが、依存性注入を検討することをお勧めします。これにより、クラスの結合が緩くなり、テストが容易になります。MarkSeemannによる.NETのDependencyInjectionという本が良い入門書であることがわかりました。

調査後、テストフレームワークとしてNUnit、モックとスタブの代わりにNSubstitute 、テストの作成を容易にするためのFluent Assertions、依存性注入のNInjectを採用しました。

于 2013-01-12T16:00:01.267 に答える
2

ApprovalTestの使用をお勧めします。これは、最適な設計ではないレガシーシステムのテストを開始するための優れたツールです。

ユニットテストと統合テストの違いを気にせず、クラスを完全に分離することを気にしないでください。あなたのコードはおそらくテスト容易性には最適ではなく、すべてを互いに分離し始めると、巨大なアレンジセクションになり、非常に壊れやすいテストになってしまいます。

一方、分離する必要のある外部リソース(Webサービス、データベース、ファイルシステムなど)があります。また、すべての非決定論的動作を分離する必要があります(Random、現在の時刻、ユーザー入力など)

検証テストのセーフティネットを作成することをお勧めします。これは、テスト容易性の方向にソフトウェアを変更するのに役立ち、コードに重大な変更を加えたかどうかを示します。

レガシーコードで効果的に機能するマイケルフェザーズを読む

于 2013-01-12T16:01:12.093 に答える
1

マイクロソフトモグラフレームワークまたはマイクロソフトフェイクフレームワークのいずれかを使用する必要があります。これらのフレームワークを使用すると、関数の動作を変更できます。

適切な単体テストでは、すべての外部メソッド呼び出しをモックして、テストするコードを分離する必要があります。Moles / Fakesは、モックオブジェクト/スタブなどを作成するのに非常に便利です。

更新:理論的には、すべての外部メソッド呼び出しをモックしてコードを分離するのが最善です。また、実際の場合でも、すべての外部メソッドをモックする必要があります。

void UpdateSomeX(X x)
{
   this.validator.Validate(x);
   x.UpdateDate = DateTime.Now;
   this.context.Attach(x);
   this.unitOfWork.Save();
}

何だと思う; 最終的に、すべての外部メソッド呼び出しをモックすることになります。次に、このメソッドをテストする必要がありますか?答えは「はい」ですが、「はい」の詳細はこの質問の範囲外です。

于 2013-01-12T15:41:07.247 に答える