【问题标题】:Automocking the SUT自动模拟 SUT
【发布时间】: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&lt;TestableExampleController&gt; 的实例,因为它找不到无参数构造函数。

无法实例化类的代理: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


【解决方案1】:

事实证明,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());
}

【讨论】:

    猜你喜欢
    • 2013-01-16
    • 1970-01-01
    • 2020-04-04
    • 2015-06-06
    • 1970-01-01
    • 2017-03-01
    • 2022-07-26
    • 2011-11-11
    相关资源
    最近更新 更多