【问题标题】:Moq an indexed property and use the index value in the return/callbackMoq 索引属性并在返回/回调中使用索引值
【发布时间】:2015-04-17 11:02:57
【问题描述】:

我想最小化一个具有索引的属性,并且我希望能够在回调中使用索引值,就像您可以在回调中使用方法参数来获取最小量的方法一样。可能最容易用一个例子来演示:

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&lt;int,int&gt;set 的回调/返回方法为Action&lt;int,int&gt;。尝试 Mike 的建议,您可以为 set 执行此操作,但有一个主要限制:

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

回调DoSet 确实是用值(23,&lt;any&gt;) 调用的。不幸的是,使用It.IsAny&lt;int&gt;() 而不是23 似乎表现得像0,而不是&lt;any&gt;

另外,我找不到用Returns 调用SetupGet 的方法,其中Returns 需要一个甚至可以编译的Func&lt;int,int&gt;

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

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

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

【问题讨论】:

  • 对我来说似乎是一个错误。使用mock.SetupSet(c =&gt; c[1] = It.IsAny&lt;int&gt;()).Callback(DoSet); 设置模拟按预期工作,mock.SetupSet(c =&gt; c[It.IsAny&lt;int&gt;()] = It.IsAny&lt;int&gt;()).Callback(DoSet); 使用MockBehavior.Strict 失败,并且对MockBehavior.Loose 不执行任何操作。
  • 使用mock.SetupSet(x =&gt; x[It.IsAny&lt;int&gt;()] = It.IsAny&lt;int&gt;()).Callback(doSet);obj[0] = 1; obj[1] = 2;,它对零索引按预期工作。它在索引 1 上失败(或其他任何事情)。
  • @Rob:你运行的是什么版本的起订量?我实际上遇到了两种不同的行为。我正在检查我在两个独立项目中运行的 Moq 版本,一个似乎可以工作,另一个没有。
  • @Mike - 我也在使用两个版本,不过为了让它正常工作,我暂时升级到 4.2.1502
  • @Rob:顺便说一句,问题不是因为起订量返回值0。库本身似乎存在一个问题,它无法使用设置索引器处理It.IsAny。我不知道为什么,但你可以尝试在他们的Github 上打开一个问题。

标签: c# moq indexed-properties


【解决方案1】:

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

因此,Moq 无法看到您使用了It.IsAny。相反,It.IsAny称为(不是我们想要的),而 Moq 只看到它的返回值恰好是 default(int)0

【讨论】:

    【解决方案2】:

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

    您遇到的问题是由It.IsAny&lt;T&gt; 调用造成的。如果使用It.IsAny&lt;T&gt;,则回调不会执行:

    [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合适的用例,但我不知道我倾向于反对它的具体细节。

    【讨论】:

    • 我应该说,我使用最小起订量的东西绝不可以被视为单元测试。
    • 您能更具体地说明您要做什么吗?虽然似乎不支持这个非常具体的场景,但如果您发布您真正想要做的事情,我们可能会找到替代方案。
    猜你喜欢
    • 2022-12-29
    • 1970-01-01
    • 2015-12-08
    • 1970-01-01
    • 2022-01-12
    • 2019-03-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多