自動モッキングに関するMark Seeman の記事を読み、その記事に基づいて再利用可能なウィンザー コンテナーを作成しています。
マークの記事の私の実装 (基本的に直接コピー)
主な作業はAutoMoqResolver
教室で行います。これにより、クラスがインターフェースに依存するたびにモックが提供されます。
public class AutoMoqResolver : ISubDependencyResolver
{
private readonly IKernel kernel;
public AutoMoqResolver(IKernel kernel)
{
this.kernel = kernel;
}
public bool CanResolve(
CreationContext context,
ISubDependencyResolver contextHandlerResolver,
ComponentModel model,
DependencyModel dependency)
{
return dependency.TargetType.IsInterface;
}
public object Resolve(
CreationContext context,
ISubDependencyResolver contextHandlerResolver,
ComponentModel model,
DependencyModel dependency)
{
var mockType = typeof(Mock<>).MakeGenericType(dependency.TargetType);
return ((Mock)this.kernel.Resolve(mockType)).Object;
}
}
は、次のインターフェイスAutoMoqResolver
の実装を使用してコンテナーに追加されます。IWindsorInstaller
public class AutoMockInstaller<T> : IWindsorInstaller
{
public void Install(
IWindsorContainer container,
IConfigurationStore store)
{
container.Kernel.Resolver.AddSubResolver(
new AutoMoqResolver(container.Kernel));
container.Register(Component.For(typeof(Mock<>)));
container.Register(Classes
.FromAssemblyContaining<T>()
.Pick()
.WithServiceSelf()
.LifestyleTransient());
}
}
次に、私のコンテナーはインストーラーを実行するだけで、単体テストでインターフェイスの依存関係のモックを自動的に提供する準備が整います。
public class AutoMockContainer<T> : WindsorContainer
{
public AutoMockContainer()
{
// simply run the auto-mock installer
this.Install(new AutoMockInstaller<T>());
}
}
素晴らしい!
これをテストしたところ、依存関係が自動的に喜んでモック化されたので、実際のコードに適用しました。これは、クラスをテストするときに従う傾向があるパターンのために、ソリューションが役に立たないことに気付いたときです。私の具体的な問題は、SUT のあるメソッドが別のメソッドから呼び出されていることを確認するために、SUT 自体を自動モックできるようにしたいということです。
テストする必要がある私のコード
例を挙げて説明します。私は MVC コードを開発しており、次の一般的なパターンを使用して目立たない AJAX をサポートしています。
public Class ExampleController : Controller
{
private IService service;
public ExampleController(IService service)
{
this.service = service;
}
public PartialViewResult DoSomethingWithAjax()
{
this.PerformTask();
return this.PartialView();
}
public RedirectToRouteResult DoSomethingWithoutAjax()
{
this.PerformTask();
return this.RedirectToAction("SomeAction");
}
protected virtual void PerformTask()
{
// do something here
}
}
私のテストパターン
PerformTask()
メソッドがDoSomethingWithAjax()
orから呼び出されたことを確認するために、次のようなDoSomethingWithoutAjax()
新しいTestableExampleController
クラスを定義します。
public class TestableExampleController : ExampleController
{
public TestableExampleController(IService service) : base(service)
{
}
public virtual void PerfomTaskPublic()
{
base.PerfomTask();
}
protected override void PerformTask()
{
this.PerformTaskPublic();
}
}
TestableExampleController
次のテストに合格するように、SUT として使用できます。
[TestMethod]
public void DoSomethingAjax_Calls_PerformTask()
{
//// Arrange
// create a mock TestableExampleController
var controllerMock = new Mock<TestableExampleController>();
controllerMock.CallBase = true;
// use the mock controller as the SUT
var sut = controllerMock.Object;
//// Act
sut.DoSomethingAjax();
//// Assert
controllerMock.Verify(x => x.PerformTaskPublic(), Times.Once());
}
私の問題
このテストをリファクタリングして、次AutoMockContainer
のようにクラスを使用してもうまくいきません:
[TestMethod]
public void DoSomethingAjax_Calls_PerformTask()
{
//// Arrange
// create a container
var container = new AutoMockContainer<TestableExampleController>();
// resolve a mock SUT using the container
var controllerMock = container.Resolve<Mock<TestableExampleController>>();
controllerMock .CallBase = true;
// use the mock controller as the SUT
var sut = controllerMock.Object;
//// Act
sut.DoSomethingAjax();
//// Assert
controllerMock.Verify(x => x.PerformTaskPublic(), Times.Once());
}
Mock<TestableExampleController>
パラメーターなしのコンストラクターが見つからないため、テストは のインスタンスの作成に失敗します。
クラス MyNamespace.TestableExampleController のプロキシをインスタンス化できません。パラメーターなしのコンストラクターが見つかりませんでした。パラメータ名: constructorArguments
私の提案する解決策
理想的には、コンテナーに登録して任意のコンポーネントに自動的にモックを提供できるラッパー クラスを実装したいと考えています。
public class ComponentWrapper<T> where T : class
{
public ComponentWrapper(Mock<T> componentMock)
{
componentMock.CallBase = true;
this.ComponentMock = componentMock;
}
public Mock<T> ComponentMock { get; private set; }
public T Component
{
get { return this.ComponentMock.Object; }
}
}
合格する次のテストを記述できるようにしたいと思います。
[TestMethod]
public void DoSomethingAjax_Calls_PerformTask()
{
//// Arrange
// create a container
var container = new AutoMockContainer<TestableExampleController>();
// resolve a ComponentWrapper using the container
var wrapper = container.Resolve<ComponentWrapper<TestableExampleController>>();
//// Act
// call a method using the component
wrapper.Component.DoSomethingAjax();
//// Assert
// verify a method call using the mock
wrapper.ComponentMock.Verify(x => x.PerformTaskPublic(), Times.Once());
}
これを達成する方法がよくわかりません。新しい ISubDependencyResolver の実装をいじるのにほとんどの時間を費やしましたが、これを機能させることができません。
うまくいけば、私の質問は明確で、答えは実際には比較的簡単ですか?