【发布时间】:2013-05-24 15:09:54
【问题描述】:
我已阅读 Mark Seeman 的 article on auto-mocking,现在我正在根据该文章编写一个可重复使用的 Windsor 容器。
我对马克文章的实现(基本直接复制)
主要工作在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;
}
}
使用IWindsorInstaller 接口的以下实现将AutoMoqResolver 添加到容器中:
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() 或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 实现,但我就是无法让它发挥作用。
希望我的问题很清楚,答案其实比较简单?
【问题讨论】:
-
这可能会有所帮助:stackoverflow.com/questions/11958110/… 如果您可以使用 AutoFixture,您可能需要添加 autofixture 标签。
-
你好 Nick,我做了一些与 Rhino Mocks 类似的事情。我这里没有代码,但我记得我使用了惰性组件加载器。这样,一旦请求,您就可以注册任何依赖项。所以你可以看看这是否对你有用,而不是子依赖解析器。
标签: c# unit-testing castle-windsor autofixture automocking