2015-04-17 41 views
7

我想moq一个有索引的属性,我希望能够在回调中使用索引值,就像你可以使用方法一样moq'd方法的回调参数。可能比较容易证明一个例子: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>。试图迈克建议,您可以排序的做到这一点的set,但有一个主要的限制:

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

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

此外,我找不到SetupGetReturns的方式,其中Returns需要Func<int,int>甚至编译。

可以使用Moq吗?

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

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

+0

看起来像一个bug给我。使用mock.SetupSet设置模拟(c => c [1] = It.IsAny ())。Callback(DoSet);'按预期工作,'mock.SetupSet(c => c [It.IsAny ()] = It.IsAny ())。回调(DoSet);'MockBehavior.Strict'失败,并且不执行'MockBehavior.Loose'。 – sloth

+0

With'mock.SetupSet(x => x [It.IsAny ()] = It.IsAny ())。回调(doSet);''和'obj [0] = 1; obj [1] = 2;'它按照零指数的预期工作。它在索引1(或其他任何事)上失败。 –

+0

@Rob:你跑哪个版本的Moq?我其实碰到了两种不同的行为。我正在检查Moq的版本,我在两个单独的项目中运行,一个似乎工作,另一个不工作。 –

回答

1

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

因此Moq看不到您使用的是It.IsAny。相反,It.IsAny(不是我们想要的)和Moq看到它的返回值只有这恰好是default(int)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,但不知道具体细节我倾向于建议反对。

+0

我应该说,我正在使用Moq for可以不被视为单元测试。 – Rob

+1

你可以对你想要做什么做更具体的描述吗?虽然这一个非常具体的情况似乎不被支持,但如果你发布了你真正想做的事情,我们可能会找到一个替代方案。 –