Moq 索引 属性 并在 return/callback 中使用索引值

Moq an indexed property and use the index value in the return/callback

我想最小起订一个 属性 有一个索引,我希望能够在回调中使用索引值,就像你可以在回调中使用方法参数一样方法。可能最容易用一个例子来证明:

public interface IToMoq
{
    int Add(int x, int y);
    int this[int x] { get; set; }
}

    Action<int, int> DoSet = (int x, int y) =>
    {
        Console.WriteLine("setting this[{0}] = {1}", x, y);
        throw new Exception("Do I ever get called?"); 
    };
    var mock = new Mock<IToMoq>(MockBehavior.Strict);

    //This works perfectly
    mock.Setup(m => m.Add(It.IsAny<int>(), It.IsAny<int>()))
        .Returns<int, int>((a, b) => a + b);

    //This compiles, but my callback never seems to be called
    mock.SetupSet(m => m[It.IsAny<int>()] = It.IsAny<int>())
        .Callback(DoSet);

    var obj = mock.Object;
    Console.WriteLine("Add(3,4) => {0}", obj.Add(3, 4));  //Works perfectly
    obj[1] = 2;   //Does not throw, why? 

编辑:澄清一下,我希望 get 的 callback/returns 方法为 Func<int,int>set 的 callback/returns 方法为是 Action<int,int>。尝试 Mike 的建议,您可以对 set 执行此操作,但有一个主要限制:

mock.SetupSet(m => m[23] = It.IsAny<int>())
            .Callback(DoSet).Verifiable();

回调 DoSet 确实随后用值 (23,<any>) 调用。不幸的是,使用 It.IsAny<int>() 而不是 23 似乎表现得像 0,而不是 <any>

此外,我找不到用 Returns 调用 SetupGet 的方法,其中 Returns 接受甚至可以编译的 Func<int,int>

是否可以为此使用最小起订量?

动机: 我只是在玩起订量,试图用它来提供流畅的 API 来执行拦截。也就是说,给定接口 II 的实例 X,自动创建行为默认为 XMock<I> 代理。

直接使用 Castle DP 可能更有意义,但我喜欢 Moq 表达式树语法。

从 .NET Framework 4.5 的 Moq 4.2.1502.0911 开始,我发现以下行为是正确的。

您 运行 遇到的问题是由于 It.IsAny<T> 调用造成的。如果使用It.IsAny<T>,回调不执行:

[Test]
public void DoesNotExecuteCallback()
{
    // Arrange
    var mock = new Mock<IIndexable>();

    var called = false;
    mock.SetupSet(m => m[It.IsAny<int>()] = It.IsAny<int>()).Callback<int,int>((x, y) => called = true);

    var instance = mock.Object;

    // Act
    instance[1] = 2;

    // Arrange
    Assert.That(called, Is.False);
}

但是如果您使用特定参数调用它,它会调用回调:

[Test]
public void DoesExecuteCallback()
{
    // Arrange
    var mock = new Mock<IIndexable>();

    var called = false;
    mock.SetupSet(m => m[1] = 2).Callback<int,int>((x, y) => called = true);

    var instance = mock.Object;

    // Act
    instance[1] = 2;

    // Arrange
    Assert.That(called, Is.True);
}

总而言之,在尝试为索引器设置期望值时应避免使用 It.IsAny。一般来说,您应该避免使用它,因为它 可以 鼓励草率的测试写作

个适合 It.IsAny 的用例,但在不知道具体细节的情况下我倾向于反对它。

方法 SetupSet 采用普通委托,而不是像许多其他 Moq 方法那样的 Expression<...> 类型的表达式树。

因此,Moq 看不到您使用了 It.IsAny。相反,It.IsAny 被称为 (不是我们想要的)并且 Moq 只看到它的 return 值,恰好是default(int),或0