使用モックフレームワークは、あなたが探しているソリューションです。一般的な問題は、依存関係が作成される場所と関係しています。通常、オブジェクト指向のプログラムを書くとき、あなたの依存関係が必要な場所で新しいキーワードを使って依存関係を直接作成するのが自然な本能です。場合によっては、コンストラクタ内に長期間の依存関係を作成することがあります。
クラスをユニット単位でテスト可能にするには、その標準を「逆転」し、外部から依存関係を作成する必要があります(または、他の依存関係のインスタンスを作成するために注入/それらの依存関係をクラスに「注入」します。 2つの最も一般的な注入メカニズムは、コンストラクタへのパラメータとして、またはセッタでのプロパティとしてのいずれかです。このように依存関係管理を外部化することで、それらの依存関係の模擬バージョンを簡単に作成して渡すことができ、アプリケーションの残りの部分から完全に分離してコード単位をテストできるようになります。
依存性注入をサポートし、管理を容易にするためにInversion of Control(IoC)コンテナが登場しました。 IoCコンテナは、これらのグラフに参加するクラスとは独立して依存関係グラフを構成するためのフレームワークです。依存関係グラフが設定されると(通常、関心のある単一のキークラスに根ざしています)、実行時にオブジェクトのインスタンスを簡単に作成することができ、必要な依存関係もすべて手動で作成することを心配する必要はありません。これは、再構成が容易で、非常に疎結合で柔軟なコードを作成するのに役立ちます。非常に優れたIoCコンテナの例はCastle Windsorです。これは、依存関係注入によってクラスを配線するための非常に豊富なフレームワークを提供します。
依存性の注入の非常に単純な例では、次のようになります。
interface ITaskService
{
void SomeOperation();
}
interface IEntityService
{
Entity GetEntity(object key);
Entity Save(Entity entity);
}
class TaskService: ITaskService
{
public TaskService(EntityServiceFactory factory)
{
m_factory = factory;
}
private EntityServiceFactory m_factory; // Dependency
public void SomeOperation() // Method must be concurrent, so create new IEntityService each call
{
IEntityService entitySvc = m_factory.GetEntityService();
Entity entity = entitySvc.GetEntity(...);
// Do some work with entity
entitySvc.Save(entity);
}
}
class EntityServiceFactory
{
public EntityServiceFactory(RepositoryProvider provider)
{
m_provider = provider;
}
private RepositoryProvider m_provider; // Dependency
public virtual IEntityService GetEntityService()
{
var repository = m_provider.GetRepository<Entity>();
return new EntityService(repository);
}
}
class EntityService: IEntityService
{
public EntityService(IEntityRepository repository)
{
m_repository = repository;
}
private IEntityRepository m_repository; // Dependency
public Entity GetEntity(object key)
{
if (key == null) throw new ArgumentNullException("key");
// TODO: Check for cached entity here?
Entity entity = m_repository.GetByKey(key);
return entity;
}
public Entity Save(Entity entity)
{
if (entity == null) throw new ArgumentNullException(entity);
if (entity.Key == null)
{
entity = m_repository.Insert(entity);
}
else
{
m_repository.Update(entity);
}
return entity;
}
}
class RepositoryProvider
{
public virtual object GetRepository<T>()
{
if (typeof(T) == typeof(Entity))
return new EntityRepository();
else if (...)
// ... etc.
}
}
interface IEntityRepository
{
Entity GetByKey(object key);
Entity Insert(Entity entity);
void Update(Entity entity);
}
class EntityRepository: IEntityRepository
{
public Entity GetByKey(object key)
{
// TODO: Load up an entity from a database here
}
public Entity Insert(Entity entity)
{
// TODO: Insert entity into database here
}
public void Update(Entity entity)
{
// TODO: Update existing entity in database here
}
}
クリス、この質問が最近頼まれました:http://stackoverflow.com/questions/1007458/writing-unit-testable-コード。これは見つけるのが簡単で、単体テストを探していました。 –
ありがとうございます。私はすばやい検索をしましたが、私が見たすべての結果は特定の単体テストに関するものでした。重複して申し訳ありません。 –