少なくとも2つの他のクラスで使用されているリポジトリクラスがあります。このリポジトリクラスは初期化する必要があります-これはコストが高くなります(データベースのクエリ)。ここで、必要な場所にリポジトリの個別のインスタンスを作成します。重要なのは、リポジトリを作成するたびに初期化する必要があるということです。そのようなリポジトリをTDD対応になるように設計するにはどうすればよいですか?私の頭の中で最初に考えたのはシングルトンでしたが、それは解決策ではありません。
5 に答える
TDD フレンドリーとは、「テスト可能な」コードを意味することを願っています。シングルトン ObjectX の場合、最も一般的な方法は、「作成の制御」の責任(SRP) を別のクラスに分割して、ObjectX が本来行うべきすべてのことを行うようにすることだと思います。
次に、すべてのクライアントに単一のインスタンスを提供する (必要に応じてスレッド同期を提供するなど) 責任を負う別のクラス ObjectXFactory または Host またはそれを呼び出したいものを用意します。
- オブジェクト X は個別に TDD できます。テスト ケースとテスト機能で新しいインスタンスを作成できます。
- 一方、ObjectXFactory も簡単にテストできます。複数の GetInstance() 呼び出しが同じオブジェクトを返すかどうかを確認するだけで済みます。または、Spring などの IOC フレームワークにこの責任を委任することをお勧めします。これにより、オブジェクト定義を宣言的にマークして、シングルトンの動作を取得できます (テストを作成する労力も節約できます)。
ObjectX コンストラクターを呼び出さないというチームの規則を教育し、準拠する必要があるだけです。常に ObjectXFactory.CreateInstance() を使用してください。(認識/規律の問題があることがわかった場合は、ObjectX の ctor を内部としてマークし、卑劣なInternalsVisibleToAttributeを介してテスト アセンブリにのみ表示されます) HTH
TDD部分の1つの答えは、モックを学ぶことです。
StephenWaltherによるこの優れた記事をチェックしてください。
http://stephenwalther.com/blog/archive/2008/03/23/tdd-introduction-to-rhino-mocks.aspx
どんなタイプのIOCコンテナを使用していますか?Unityは私の選択したコンテナであり、クラスをシングルトンにするContainerControledLifetimeManagerが含まれていますが、自分で管理することはできません。
シングルトンを検討する前に、パフォーマンスを向上させるためにインスタンスをキャッシュすることを検討してください。ただし、TDDに適した設計では、テストのために「遅い」ビットを削除してスタブやモックに置き換えることができるように、戦略インジェクションを検討してください。可能であれば、テストでdb呼び出しを行わないようにしてください。
それはできません -- 少なくとも真の TDD の意味では。
Unity などの DI/IoC 戦略に依存するということは、テストが外部コンポーネントに依存し、独立してテストされないことを意味します。
その後、テストは単体テストではなく統合テストになります。
==以下の回答は無視してください==
リポジトリをテスト可能にする方法を知りたかったのでしょう。
そのためのインターフェースを導入すると、それをモックまたはスタブ化できるようになり、リポジトリの具体的な実装とは無関係にオブジェクトをテストできるようになります。
Rhino Mocks 3.5 for .NET 3.5を使用してこれを説明します。
リポジトリからインターフェイスを作成しましょう、それを呼び出しましょうIRepository
public interface IRepository
{
}
ここで、2 つの異なるオブジェクトに IRepository を使用する必要があるため、ジェネリックを使用して、リポジトリをインスタンス化できるようにします。
public interface IRepository<T>
もちろん、それはある種の find メソッドがあることを意味します:
{
public IEnumerable<T> Find(Criteria criteria)
}
where your criteria オブジェクトは、何を探すかを設定できるオブジェクトです。たとえば、 where 句です。
これで、オブジェクトができました。
public class SomeObject
{
IRepository<SomeObject> repository;
public SomeObject(){}
public IRepository<SomeObject> repository { get; set; }
IEnumerable<SomeObject> FindAll()
{
//let's assume new Criteria() will return all results
return respository.Find(new Criteria());
}
}
SomeObject
FindAll() が予想される一連の結果を返すようにテストしたい場合 -- これが Rhino モックの出番です:
[TestFixture]
public class SomeObjectTests
{
[Test]
public void TestSomeObjectFindAll()
{
IRepository<SomeObject> stubRepository = MockRepsitory.GenerateStub<IRepsitory<SomeObject>>();
stubRepository.Expect(r => r.Find(new Criteria())
.Return(new List<SomeObject>{
new SomeObject(),
new SomeObject(),
new SomeObject());
var testObject = new SomeObject { Repository = stubRepository };
IList<SomeObject> findAllResult = testObject.FindAll();
//returned list contains 3 elements, as expected above
Assert.AreEqual(3, findAllResult.Count)
}
}
上記のコードは、すべての点で TDD のベスト プラクティスではありませんが、開始する場所であることに注意してください。
ここでの重要な概念は、特にオブジェクトがデータベースやファイル システムなどにアクセスする傾向がある場合に、オブジェクトの疎結合を可能にするインターフェイスを導入することです。
Rhino Mocks に関する Ben Hall の記事には、より包括的な例とより良い例があります。