Moq:如何获取传递给模拟服务方法的参数

时间:2010-07-17 00:39:25

标签: c# moq

想象一下这个班级

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的内容?

8 个答案:

答案 0 :(得分:229)

您可以使用Mock.Callback方法:

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

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

如果你只想在传入的参数中检查一些简单的东西,你也可以直接进行:

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

答案 1 :(得分:19)

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

验证非常强大,值得花些时间习惯。

答案 2 :(得分:15)

Gamlor的回答对我有用,但我想我会扩展John Carpenter的评论,因为我正在寻找一个涉及多个参数的解决方案。我认为偶然发现此页面的其他人可能处于类似情况。我在Moq documentation

中找到了此信息

我将使用Gamlor的示例,但让我们假装AsyncHandle方法有两个参数:stringSomeResponse对象。

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表达式。

答案 3 :(得分:11)

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

答案 4 :(得分:4)

您可以使用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 )));

答案 5 :(得分:0)

这也有效:

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

答案 6 :(得分:0)

还可以使用Capture.In的{​​{1}}功能。 OOTB moq功能可以在集合中捕获参数。

moq

答案 7 :(得分:0)

这里有很多好的答案!使用开箱即用的Moq功能集,直到需要对传递给依赖项的几个类参数进行断言为止。如果最终遇到这种情况,它的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提供的功能可以在声明时实现相同的功能,并提供故障隔离功能,那我会很高兴。手指交叉!