Nic*_*per 3 c# unit-testing castle-windsor autofixture automocking
我已经阅读了Mark Seeman 关于自动模拟的文章,我现在正在编写一个基于该文章的可重复使用的windsor容器.
我执行Mark的文章(基本上直接复制)
主要工作是在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;
}
}
Run Code Online (Sandbox Code Playgroud)
将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());
}
}
Run Code Online (Sandbox Code Playgroud)
然后我的容器只运行安装程序,它就可以自动为单元测试中的任何接口依赖项提供模拟:
public class AutoMockContainer<T> : WindsorContainer
{
public AutoMockContainer()
{
// simply run the auto-mock installer
this.Install(new AutoMockInstaller<T>());
}
}
Run Code Online (Sandbox Code Playgroud)
超!
我已经测试了这个,并且我的依赖项被自动地模拟了,所以我然后将它应用于一些真正的代码.这是当我意识到解决方案对我没有帮助时,因为我在测试课时倾向于遵循这种模式.我的具体问题是我希望能够自动模拟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
}
}
Run Code Online (Sandbox Code Playgroud)
我的测试模式
所以为了验证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();
}
}
Run Code Online (Sandbox Code Playgroud)
然后我可以使用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());
}
Run Code Online (Sandbox Code Playgroud)
我的问题
重构此测试以使用我的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());
}
Run Code Online (Sandbox Code Playgroud)
测试无法创建实例,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; }
}
}
Run Code Online (Sandbox Code Playgroud)
我希望能够编写以下通过的测试:
[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());
}
Run Code Online (Sandbox Code Playgroud)
我不能完全理解如何实现这一目标,而且我花了大部分时间摆弄新的ISubDependencyResolver实现,但我无法让它工作.
希望我的问题很明确,答案实际上相对简单?
事实证明,AutoFixture.AutoMoq将完全符合我的要求,所以感谢TrueWill指出我正确的方向.
以下简单测试将通过:
[TestMethod]
public void Run_Calls_DoSomethingProtected()
{
//// Arrange
// AutoMoqCustomization allows AutoFixture to
// be used an an auto-mocking container
var fixture = new Fixture().Customize(new AutoMoqCustomization());
// simply ask the fixture to create a mock
var sutMock = fixture.Create<Mock<TestableDummySystem>>();
//// Act
// exercise the mock object
sutMock.Object.Run();
//// Assert
// this verification passes!
sutMock.Verify(x => x.DoSomethingProtectedPublic());
}
Run Code Online (Sandbox Code Playgroud)