使用 Moq 和 Castle Windsor 进行部分模拟

Son*_*oul 5 dependency-injection castle-windsor moq ioc-container

\n

版本 \xc2\xa0\xc2\xa0 温莎城堡 3.3.0.0 \xc2\xa0\xc2\xa0 起订量 4.2.1507.118

\n
\n\n

看来 Moq 附带了CallBase设置,启用后,会将对任何非设置方法的调用委托给基础对象。

\n\n
var mock = new Mock<MyService> {CallBase = true};\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
         Container.Register(Component\n            .For<MyService>()\n            .Instance(mock.Object)\n            .LifeStyle.Singleton\n            );\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

所以当我这样做时:

\n\n
mock.setup(x => x.SomeMethod).Returns(myMockedData);\n
Run Code Online (Sandbox Code Playgroud)\n\n

这按预期工作:

\n\n
Container.Resolve<MyService>().SomeMethod();\n
Run Code Online (Sandbox Code Playgroud)\n\n

但现在返回一个空值:

\n\n
Container.Resolve<MyService>().AnotherMethod(); \n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

更具体的代码示例

\n\n
public class Auth : IAuth\n{\n   // mocked succesfully\n   public IEnumerable<T> AuthorizeData<T>(IEnumerable<T> data)  { ... }\n\n    // never gets called\n    public virtual List<User> GetAllUsers() {\n        return new List<User>{new User{FIRST_NAME = "test"}};\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

模拟方法

\n\n
    protected static Mock<T> GetMock<T>() where T : class {\n\n        var mock = new Mock<T> {CallBase = true, DefaultValue = DefaultValue.Mock};\n        IoC.Container.Register(Component\n            .For<T>()\n            .Instance(mock.Object)\n            .LifeStyle.Transient\n            );\n        return mock;\n    }\n
Run Code Online (Sandbox Code Playgroud)\n\n

模拟设置

\n\n
GetMock<IAuth>().Setup(x => x.AuthorizeData(It.IsAny<IEnumerable<DO>>()))\n                .Returns((IEnumerable<DO> data) => data); // pass through input data (do not authorize\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n

Mar*_*ann 2

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

var mock = new Mock<MyService> { CallBase = true, DefaultValue = DefaultValue.Mock };
Run Code Online (Sandbox Code Playgroud)

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

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


您可能想考虑将温莎城堡变成自动模拟容器


根据下面的评论,我无法重现该问题。假设AuthorizeData是这样virtual(上面没有,但评论声称),这个测试通过了:

[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());
}
Run Code Online (Sandbox Code Playgroud)