最小起订量中的设置序列

Posted

技术标签:

【中文标题】最小起订量中的设置序列【英文标题】:SetupSequence in Moq 【发布时间】:2012-07-03 17:34:41 【问题描述】:

我想要一个第一次返回0 的模拟,然后在此后调用该方法的任何时候返回1。问题是如果方法被调用4次,我得写:

mock.SetupSequence(x => x.GetNumber())
    .Returns(0)
    .Returns(1)
    .Returns(1)
    .Returns(1);

否则,该方法返回null。

有什么办法可以写,在初始调用之后,方法返回1

【问题讨论】:

【参考方案1】:

聚会有点晚了,但如果你还想使用 Moq 的 API,你可以在最后一次 Returns 调用的操作中调用 Setup 函数:

var mock = new Mock<IFoo>();
mock.SetupSequence(m => m.GetNumber())
    .Returns(4)
    .Returns(() =>
    
        // Subsequent Setup or SetupSequence calls "overwrite" their predecessors: 
        // you'll get 1 from here on out.
        mock.Setup(m => m.GetNumber()).Returns(1);
        return 1;
    );

var o = mock.Object;
Assert.Equal(4, o.GetNumber());
Assert.Equal(1, o.GetNumber());
Assert.Equal(1, o.GetNumber());
// etc...

我想演示使用 StepSequence,但对于 OP 的特定情况,您可以简化并将所有内容放在 Setup 方法中:

mock.Setup(m => m.GetNumber())
    .Returns(() =>
    
        mock.Setup(m => m.GetNumber()).Returns(1);
        return 4;
    );

xunit@2.4.1Moq@4.14.1 在这里测试了所有东西 - 通过 ✔

【讨论】:

这似乎不适用于“ReturnsAsync”。您不能在方法中使用 lamda 表达式作为参数。也许它适用于非异步开发,但现在谁在做呢? 问题不在于异步开发?【参考方案2】:

只需设置一个扩展方法,例如:

public static T Denqueue<T>(this Queue<T> queue)

    var item = queue.Dequeue();
    queue.Enqueue(item);
    return item;

然后像这样设置返回:

var queue = new Queue<int>(new []0, 1, 1, 1);
mock.Setup(m => m.GetNumber).Returns(queue.Denqueue);

【讨论】:

【参考方案3】:

最干净的方法是创建一个Queue 并将.Dequeue 方法传递给Returns

.Returns(new Queue&lt;int&gt;(new[] 0, 1, 1, 1 ).Dequeue);

【讨论】:

@RomainVerdier - 不,它没有。我认为 OP 要求提供 4 次调用的解决方案。 All - 避免我最初的错误。如果您将其定义为具有 Returns(myQueue.Dequeue()) ,那么您只会得到第一个结果 - 因为您实际上已将结果出队,而不是提供 lambda 表达式。 @sfuqua - 这就是我的回答使用委托而不是调用的原因。 @JakubKonecki 是的,完全正确。我只是警告其他人要精确地遵循这种模式,不要像我那样意外地 Dequeue()。 @ben 我喜欢在测试结束时将我的异常抛出 Moq 语句(验证)放在 Assert 块(Arrange,Act,Assert)中,在那里您可以干净地检查是否调用了某些东西确切的次数【参考方案4】:

您可以使用临时变量来跟踪方法被调用的次数。

例子:

public interface ITest
 Int32 GetNumber(); 

static class Program

    static void Main()
    
        var a = new Mock<ITest>();

        var f = 0;
        a.Setup(x => x.GetNumber()).Returns(() => f++ == 0 ? 0 : 1);

        Debug.Assert(a.Object.GetNumber() == 0);
        for (var i = 0; i<100; i++)
            Debug.Assert(a.Object.GetNumber() == 1);
    

【讨论】:

【参考方案5】:

这不是特别花哨,但我认为它会起作用:

    var firstTime = true;

    mock.Setup(x => x.GetNumber())
        .Returns(()=>
                        
                            if(!firstTime)
                                return 1;

                            firstTime = false;
                            return 0;
                        );

【讨论】:

我认为这不是线程安全的,所以如果您的测试涉及从不同线程调用 GetNumber 的代码 - 您可能会多次返回 1。为实现线程安全,Interlocked 类可用于firstTime 的读/写。 虽然对于给定的场景来说它是相当安全的,但在测试逻辑中使用 if 语句不应该太舒服。 解释:typemock.com/avoid-logic-inside-a-unit-test

以上是关于最小起订量中的设置序列的主要内容,如果未能解决你的问题,请参考以下文章

起订量中 VerifyAll() 的用途是啥?

如何最小起订量实体框架 SqlQuery 调用

使用最小起订量模拟静态属性

如何最小起订量委托回调和 lambda 方法

使用最小起订量测试 Polly 重试策略

无法为 MediatR 设置起订量回调