簡體   English   中英

Moq:如何獲取傳遞給模擬服務方法的參數

[英]Moq: How to get to a parameter passed to a method of a mocked service

想象這個班級

public class Foo {

    private Handler _h;

    public Foo(Handler h)
    {
        _h = h;
    }

    public void Bar(int i)
    {
        _h.AsyncHandle(CalcOn(i));
    }

    private SomeResponse CalcOn(int i)
    {
        ...;
    }
}

Mo(q)cking Handler 在 Foo 測試中,我如何能夠檢查Bar()已傳遞給_h.AsyncHandle

您可以使用 Mock.Callback 方法:

var mock = new Mock<Handler>();
SomeResponse result = null;
mock.Setup(h => h.AsyncHandle(It.IsAny<SomeResponse>()))
    .Callback<SomeResponse>(r => result = r);

// do your test
new Foo(mock.Object).Bar(22);
Assert.NotNull(result);

如果您只想檢查傳入參數上的一些簡單內容,您也可以直接進行:

mock.Setup(h => h.AsyncHandle(It.Is<SomeResponse>(response => response != null)));

另一種方法是使用Capture.In ,這是 Moq 中的開箱即用功能,可讓您將參數捕獲到集合中。

//Arrange
var args = new List<SomeResponse>();
mock.Setup(h => h.AsyncHandle(Capture.In(args)));

//Act
new Foo(mock.Object).Bar(22);

//Assert
//... assert args.Single() or args.First()

Gamlor 的回答對我有用,但我想我會擴展 John Carpenter 的評論,因為我正在尋找一種涉及多個參數的解決方案。 我認為偶然發現此頁面的其他人可能處於類似的情況。 我在Moq 文檔中找到了這個信息。

我將使用 Gamlor 的示例,但讓我們假設 AsyncHandle 方法接受兩個參數:一個string和一個SomeResponse對象。

var mock = new Mock<Handler>();
string stringResult = string.Empty;
SomeResponse someResponse = null;
mock.Setup(h => h.AsyncHandle(It.IsAny<string>(), It.IsAny<SomeResponse>()))
    .Callback<string, SomeResponse>((s, r) => 
    {
        stringResult = s;
        someResponse = r;
    });

// do your test
new Foo(mock.Object).Bar(22);
Assert.AreEqual("expected string", stringResult);
Assert.IsNotNull(someResponse);

基本上,您只需要添加另一個具有適當類型的It.IsAny<>() ,向Callback方法添加另一個類型,並根據需要更改 lambda 表達式。

Callback 方法肯定會起作用,但是如果您在具有大量參數的方法上執行此操作,則可能會有點冗長。 這是我用來刪除一些樣板的東西。

var mock = new Mock<Handler>();

// do your test   
new Foo(mock.Object).Bar(22);

var arg = new ArgumentCaptor<SomeResponse>();
mock.Verify(h => h.AsyncHandle(arg.Capture()));
Assert.NotNull(arg.Value);

這是 ArgumentCaptor 的來源:

public class ArgumentCaptor<T>
{
    public T Capture()
    {
        return It.Is<T>(t => SaveValue(t));
    }

    private bool SaveValue(T t)
    {
        Value = t;
        return true;
    }

    public T Value { get; private set; }
}

Gamlor 的答案有效,但另一種方法(我認為在測試中更具表現力的方法)是......

var mock = new Mock<Handler>();
var desiredParam = 47; // this is what you want to be passed to AsyncHandle
new Foo(mock.Object).Bar(22);
mock.Verify(h => h.AsyncHandle(desiredParam), Times.Once());

驗證非常強大,值得花時間習慣。

您可以使用It.Is<TValue>()匹配器。

var mock = new Mock<Handler>();
new Foo(mock.Object).Bar(22);
mock.Verify(h => h.AsyncHandle(It.Is<SomeResponse>(r => r != null )));

這也有效:

Mock<InterfaceThing> mockedObject = new Mock<InterfaceThing>();
var objectParameter = mockedObject.Invocations[1].Arguments[0] as ObjectParameter;

這里有很多很好的答案! 使用開箱即用的 Moq 功能集,直到您需要對傳遞給依賴項的幾個類參數進行斷言。 如果您最終遇到這種情況,帶有 It.Is 匹配器的 Moq 驗證功能並不能很好地隔離測試失敗,並且返回/回調捕獲參數的方式會為您的測試添加不必要的代碼行(和長時間的測試對我來說是不行的)。

這是一個要點: https ://gist.github.com/Jacob-McKay/8b8d41ebb9565f5fca23654fd944ac6b 帶有我編寫的 Moq (4.12) 擴展,它提供了一種更具聲明性的方式來對傳遞給模擬的參數進行斷言,而沒有上述缺點。 這是驗證部分現在的樣子:

        mockDependency
            .CheckMethodWasCalledOnce(nameof(IExampleDependency.PersistThings))
            .WithArg<InThing2>(inThing2 =>
            {
                Assert.Equal("Input Data with Important additional data", inThing2.Prop1);
                Assert.Equal("I need a trim", inThing2.Prop2);
            })
            .AndArg<InThing3>(inThing3 =>
            {
                Assert.Equal("Important Default Value", inThing3.Prop1);
                Assert.Equal("I NEED TO BE UPPER CASED", inThing3.Prop2);
            });

如果 Moq 提供了一個功能來完成同樣的事情,同時作為聲明性並提供故障隔離,我會很高興。 手指交叉!

暫無
暫無

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

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