Moq是一个索引属性,并使用返回/回调中的索引值

时间:2015-04-17 11:02:57

标签: c# moq indexed-properties

我想要moq一个具有索引的属性,并且我希望能够在回调中使用索引值,就像在moq方法的回调中使用方法参数一样。可能最容易用一个例子来证明:

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的回调/返回方法为Func<int,int>,并且set的回调/返回方法为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>

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

是否可以使用Moq?

动机:我只是在玩Moq,试图用它来提供一个流畅的API来执行拦截。也就是说,给定I的接口XI的实例Mock<I>,会自动创建一个行为默认为X的{​​{1}}代理。

直接使用Castle DP可能更有意义,但我喜欢Moq Expression Tree语法。

2 个答案:

答案 0 :(得分:1)

从.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的用例,但我不知道我倾向于推荐的具体内容。

答案 1 :(得分:1)

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

出于这个原因,Moq看不到你使用了It.IsAny。相反,It.IsAny 称为 (不是我们想要的),而Moq只看到它的返回值恰好是default(int)或{{1 }}