いくつかのインターフェイスを解決するときに、いくつかの複雑なオブジェクト構築を行う IoC コンテナーがあります。単体/統合テストでこれらのインターフェイスの実装を使用したいと考えています。IoC コンテナーを使用したテストでこれらのインターフェイスを解決することに何か問題がありますか? それとも、この状況でインスタンスを手動で構築する必要があると予想されますか?
1 に答える
私たちが関心のあるクラスをユニットテストしているとき、 「クラスは私たちがやりたいことをしますか」。私たちの出発点は、完全に構築されたインスタンスです。統合テストの質問と見なされる場合もありますが、ユニットテストの質問はありません。
私たちが持っていると言う、
A
A(IB b, IC c)
B : IB
B(ID d)
C : IC
D : ID
ここ
IB
で、はのインターフェイス、B
はの
IC
インターフェイスC
、
ID
はのインターフェイスですD
。
ユニットテストの場合A
、B
使用ID
することは重要ではありません(そうでない場合は、インターフェイスを確認する必要があります。A
アクセス権IB.D.xxx()
は良くありません)。必要なのは、A
との実装(モック/スタブ)をIB
提供することだけですIC
。
の単体テストに関するA
限り、A
インスタンスが手動で作成されたのか、コンテナによって作成されたのかは重要ではありません。どちらの方法でも同じオブジェクトを取得します。
最初のレベルの依存関係としてモックを渡す限り、コンテナを使用してオブジェクトを手動で作成する場合、保存はありません。保存は、IOCを使用してオブジェクトグラフを作成している場合にのみ発生しますが、これを実行している場合は、統合テストを行っています。これは必ずしも悪いことではありませんが、目標を明確にする必要があります。
上記の単体テストでは、次の単体テストを作成します
D
C
B (passing in a mocked/stubbed ID)
A (passing in mocked/stubbed IC and IB)
ユニットテストの場合A
、からの正解を最大までD
渡す必要はありません。
私たちが気にするのは、Aのロジックが期待どおりに機能することです。たとえば、パラメーターyとzを使用して呼び出し、の結果を返します。正しい答え(たとえば、のロジックに依存するもの)が得られることを確認している場合は、単体テストを過ぎて統合テストに入っています。B
A
A
IB.x()
IB.x()
D
結論テスト 対象のユニットが適切に分離されている限り、
テスト対象のユニットがIOCコンテナによって作成されたか、手動で作成されたかは関係ありません。コンテナを使用してオブジェクトグラフを作成している場合は、統合テストに参加している可能性が高くなります(および/またはクラス間の結合が多すぎるという問題があります-呼び出し)A
IB.D.xxx()
統合テストのモック
警告:以下のいくつかは、使用中のIOCコンテナに依存しています。Unityを使用する場合、最後の登録が「優先」されます。これが他の人にも当てはまるかどうかはわかりません。
問題のミニマリストシステムでは、
A
A (IB b)
B : IB
B
私たちの「葉」です。外界と通信します(たとえば、ネットワークストリームから読み取ります)。統合テストでは、これをモックしたいと思います。
ライブシステムの場合、を使用してServiceLocatorを設定しCreateContainerCore()
ます。これには、の「ライブ」実装の登録が含まれIB
ます。
のモックバージョンを必要とする統合テストを実行する場合、モックオブジェクトをラップして登録するIB
ためのコンテナを設定します。CreateContainerWithMockedExternalDependencies()
CreateContainerCore()
IB
以下のコードは大幅に簡略化されていますが、形状は必要な数のクラスと依存関係にまで拡張されています。実際には、サービスロケーターのセットアップ、モック/スタブクラス、検証目的でのモックへのアクセス、およびその他のハウスキーピングを支援する基本テストクラスがあります(たとえば、各テストでServiceLocatorプロバイダーを明示的に設定する必要はありません)。
[TestClass]
public class IocIntegrationSetupFixture {
[TestMethod]
public void MockedB() {
ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(CreateContainerWithMockedExternalDependencies()));
var a = ServiceLocator.Current.GetInstance<A>();
Assert.AreEqual("Mocked B", a.GetMessage());
}
[TestMethod]
public void LiveB() {
ServiceLocator.SetLocatorProvider(() => new UnityServiceLocator(CreateContainerCore()));
var a = ServiceLocator.Current.GetInstance<A>();
Assert.AreEqual("Live B", a.GetMessage());
}
private IUnityContainer CreateContainerCore() {
var container = new UnityContainer();
container.RegisterType<IB, B>(new ContainerControlledLifetimeManager());
return container;
}
private IUnityContainer CreateContainerWithMockedExternalDependencies() {
var container = CreateContainerCore();
var mockedB = new Mock<IB>();
mockedB.SetupGet(mk => mk.Message).Returns("Mocked B");
container.RegisterInstance<IB>(mockedB.Object);
return container;
}
public class A {
private IB _b;
public A(IB b) {
_b = b;
}
public string GetMessage() {
return _b.Message;
}
}
public interface IB {
string Message { get; }
}
private class B : IB {
public string Message {
get { return "Live B"; }
}
}
}