簡體   English   中英

Moq 中的 SetupSequence

[英]SetupSequence in Moq

我想要一個第一次返回0的模擬,然后在以后調用該方法時返回1 問題是如果該方法被調用 4 次,我必須寫:

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

否則,該方法返回 null。

有沒有什么辦法可以寫出,在初始調用之后,該方法返回1

最干凈的方法是創建一個Queue並將.Dequeue方法傳遞給Returns

.Returns(new Queue<int>(new[] { 0, 1, 1, 1 }).Dequeue);

這不是特別花哨,但我認為它會起作用:

    var firstTime = true;

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

                            firstTime = false;
                            return 0;
                        });

聚會有點晚了,但如果您仍想使用 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在這里測試了所有內容 - 通過 ✔

只需設置一個擴展方法,如:

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);

您可以使用臨時變量來跟蹤調用該方法的次數。

例子:

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);
    }
}

通常情況下,我不會為這樣一個老問題提交新的答案,但近年來 ReturnsAsync 變得非常普遍,這使得潛在的答案變得更加復雜。

正如其他人所說,您基本上可以只創建一個結果隊列,並在您的 Returns 調用中傳遞 queue.Dequeue 委托。

例如。

var queue = new Queue<int>(new []{0,1,2,3});
mock.SetupSequence(m => m.Bar()).Returns(queue.Dequeue);

但是,如果您正在設置異步方法,我們通常應該調用 ReturnsAsync。 queue.Dequeue 傳遞到 ReturnsAsync 時,將導致對正在設置的方法的第一次調用正常工作,但隨后的調用將引發 Null 引用異常。 您可以像其他一些示例一樣創建您自己的返回任務的擴展方法,但是這種方法不適用於 SetupSequence,並且必須使用 Returns 而不是 ReturnsAsync。 此外,必須創建一個擴展方法來處理返回結果,這違背了使用 Moq 的初衷。 在任何情況下,如果您已將委托傳遞給 Returns 或 ReturnsAsync,任何返回類型為 Task 的方法在通過 SetupSequence 設置時在第二次調用時總是會失敗。

然而,有兩種有趣的替代方法可以替代這種方法,它們只需要最少的附加代碼。 第一個選項是識別 Mock 對象的 Setup 和 SetupAsync 遵循 Fluent Api 設計模式。 這意味着,從技術上講,Setup、SetupAsync、Returns 和 ReturnsAsync 實際上都返回一個“Builder”object。我所說的 Builder 類型 object 是流暢的 api 樣式對象,例如 QueryBuilder、StringBuilder、ModelBuilder 和 IServiceCollection/IServiceProvider . 這樣做的實際結果是我們可以輕松地做到這一點:

var queue = new List<int>(){0,1,2,3};
var setup = mock.SetupSequence(m => m.BarAsync());
foreach(var item in queue)
{
  setup.ReturnsAsync(item);
}

這種方法允許我們同時使用 SetupSequence 和 ReturnsAsync,在我看來這遵循更直觀的設計模式。

第二種方法是認識到 Returns 能夠接受返回 Task 的委托,並且 Setup 將始終返回相同的東西。 這意味着如果我們要像這樣為 Queue 創建一個擴展方法:

public static class EMs
{
  public static async Task<T> DequeueAsync<T>(this Queue<T> queue)
  {
    return queue.Dequeue();
  }
}

然后我們可以簡單地寫:

var queue = new Queue<int>(new []{0,1,2,3});
mock.Setup(m => m.BarAsync()).Returns(queue.DequeueAsync);

或者可以使用 Microsoft.VisualStudio.Threading 中的 AsyncQueue class,這將允許我們這樣做:

var queue = new AsyncQueue<int>(new []{0,1,2,3});
mock.Setup(m => m.BarAsync()).Returns(queue.DequeueAsync);

導致所有這一切的主要問題是,當到達設置序列的末尾時,該方法被視為未設置。 為避免這種情況,如果在到達序列末尾后應返回結果,您還應調用標准設置。

我整理了一個關於此功能的相當全面的小提琴,其中包含您在做錯事時可能遇到的錯誤示例,以及您可以正確做事的幾種不同方式的示例。 https://do.netfiddle.net/KbJlxb

Moq 使用構建器模式,設置模擬的行為。 可以,但不必使用流暢的界面 牢記這一點,我以這種方式解決了問題:

var sequence = myMock
            .SetupSequence(""the method I want to set up"");

foreach (var item in stuffToReturn)
{    
  sequence = sequence.Returns(item);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM