【问题标题】:partial mocking with Moq and Castle WindsorMoq 和 Castle Windsor 的部分嘲弄
【发布时间】:2015-10-22 04:40:24
【问题描述】:

版本    温莎城堡 3.3.0.0    Moq 4.2.1507.118

Moq 似乎带有 CallBase 设置,启用后,将对任何非设置方法的调用委托给基础对象。

var mock = new Mock<MyService> {CallBase = true};

不幸的是,一旦我在 Castle Windsor 注册它,所有非模拟方法都会立即返回 null,不再调用底层逻辑。

         Container.Register(Component
            .For<MyService>()
            .Instance(mock.Object)
            .LifeStyle.Singleton
            );

我也尝试了几种不同的生活方式。

所以当我这样做时:

mock.setup(x => x.SomeMethod).Returns(myMockedData);

这按预期工作:

Container.Resolve<MyService>().SomeMethod();

但现在返回一个空值:

Container.Resolve<MyService>().AnotherMethod(); 

一旦我删除了模拟注册,AnotherMethod 就会像以前一样开始工作。

更具体的代码示例

public class Auth : IAuth
{
   // mocked succesfully
   public IEnumerable<T> AuthorizeData<T>(IEnumerable<T> data)  { ... }

    // never gets called
    public virtual List<User> GetAllUsers() {
        return new List<User>{new User{FIRST_NAME = "test"}};
    }
}

模拟方法

    protected static Mock<T> GetMock<T>() where T : class {

        var mock = new Mock<T> {CallBase = true, DefaultValue = DefaultValue.Mock};
        IoC.Container.Register(Component
            .For<T>()
            .Instance(mock.Object)
            .LifeStyle.Transient
            );
        return mock;
    }

模拟设置

GetMock<IAuth>().Setup(x => x.AuthorizeData(It.IsAny<IEnumerable<DO>>()))
                .Returns((IEnumerable<DO> data) => data); // pass through input data (do not authorize

再次总结,AuthorizeData被mock成功,GetAllUsers()返回一个空列表。并且原始类永远不会被调用

【问题讨论】:

    标签: dependency-injection moq castle-windsor ioc-container


    【解决方案1】:

    CallBase 不控制返回值的生成方式。为此,您需要设置 DefaultValue 属性:

    var mock = new Mock<MyService> { CallBase = true, DefaultValue = DefaultValue.Mock };
    

    DefaultValue(原文如此)的默认值为DefaultValue.Empty,因此您需要将其显式设置为DefaultValue.Mock。

    当您这样做时,mock 将在没有显式设置的情况下从方法返回其他代理值。如果返回值是具体类型,Moq 将尝试创建该类型的值,但仅当该类型具有无参数构造函数时才会这样做。如果不存在无参构造函数,则在调用该方法时会抛出异常。


    您可能想考虑将温莎城堡变成Auto-Mocking Container。


    根据下面的 cmets,我无法重现该问题。假设 AuthorizeData 是 virtual(它不在上面,但 cmets 声称),这个测试通过了:

    [Fact]
    public void Repro()
    {
        var td = new Mock<Auth>
        {
            CallBase = true,
            DefaultValue = DefaultValue.Mock
        };
        var container = new WindsorContainer();
        container.Register(Component.For<Auth>().Instance(td.Object));
    
        td
            .Setup(a => a.AuthorizeData<string>(new string[0]))
            .Returns(new[] { "foo", "bar" });
    
        Assert.Equal(
            new[] { "foo", "bar" },
            container.Resolve<Auth>().AuthorizeData(new string[0]));
        Assert.NotEmpty(container.Resolve<Auth>().GetAllUsers());
    }
    

    【讨论】:

    • 嗨,感谢您的来信。到目前为止,我看到此属性集的唯一行为变化是我的方法返回一个空列表而不是 null。我在实际服务中的断点没有被命中,让我相信它仍然没有通过调用
    • @SonicSoul 哪个列表?什么类型?
    • 马克,我的意思是当调用 Container.Resolve().AnotherMethod();此方法返回一个 Person 类型对象的 List。这个方法没有设置,所以我对 CallBase 的理解是它应该调用底层类,但我的底层类永远不会被调用。我在那里有一个永远不会被击中的断点。我怀疑问题可能与模拟在温莎城堡注册的方式有关?
    • @SonicSoul 请分享MyService。
    • 如给定的那样,AuthorizeData 不能被覆盖,因为它不是虚拟的,所以我无法重现该问题。另外,你能please slim down the repro code.
    猜你喜欢
    • 2017-12-27
    • 2018-12-24
    • 2018-02-14
    • 2019-08-03
    • 2017-09-01
    • 1970-01-01
    • 2014-10-27
    • 1970-01-01
    • 2016-02-26
    相关资源
    最近更新 更多