你能帮我理解Moq Callback吗?

时间:2010-05-14 09:44:35

标签: .net moq

使用Moq查看Callback,但我找不到一个简单的例子来了解如何使用它。

您是否有一个小的工作片段,清楚地解释了如何以及何时使用它?

5 个答案:

答案 0 :(得分:68)

难以击败https://github.com/Moq/moq4/wiki/Quickstart

如果这还不够清楚,我会称之为doc bug ......

编辑:回应你的澄清......

对于您执行的每个模拟方法Setup,您需要指出以下内容:

  • 对输入的限制
  • 要导出返回值(如果有)的/ way的值

.Callback机制说“我现在无法描述它,但是当这样的呼叫发生时,给我回电话,我会做需要做的事情”。作为同一个流畅的调用链的一部分,您可以通过.Returns来控制结果返回(如果有的话)。在QS示例中,一个示例是它们使每次返回的值增加。

通常,您不需要经常使用这样的机制(xUnit测试模式具有针对测试条件逻辑的反模式的术语),并且如果有任何更简单或内置的方式来确定您需要的内容,它应该被优先使用。

Part 3 of 4 in Justin Etheredge's Moq series涵盖了它,there's another example of callbacks here

答案 1 :(得分:48)

以下是使用回调测试发送到处理插入的数据服务的实体的示例。

var mock = new Mock<IDataService>();
DataEntity insertedEntity = null;

mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1) 
           .Callback((DataEntity de) => insertedEntity = de);

替代通用方法语法:

mock.Setup(x => x.Insert(It.IsAny<DataEntity>())).Returns(1) 
           .Callback<DataEntity>(de => insertedEntity = de);

然后你可以测试像

这样的东西
Assert.AreEqual("test", insertedEntity.Description, "Wrong Description");

答案 2 :(得分:7)

moq中有两种Callback。一个在呼叫返回之前发生;另一个在调用返回后发生。

var message = "";
mock.Setup(foo => foo.Execute(arg1: "ping", arg2: "pong"))
    .Callback((x, y) =>
    {
        message = "Rally on!";
        Console.WriteLine($"args before returns {x} {y}");
    })
    .Returns(message) // Rally on!
    .Callback((x, y) =>
    {
        message = "Rally over!";
        Console.WriteLine("arg after returns {x} {y}");
    });

在两个回调中,我们都可以:

  1. 检查方法参数
  2. 捕获方法arguemnts
  3. 更改上下文状态

答案 3 :(得分:2)

Callback只是在调用其中一个模拟方法时执行任何自定义代码的方法。这是一个简单的例子:

public interface IFoo
{
    int Bar(bool b);
}

var mock = new Mock<IFoo>();

mock.Setup(mc => mc.Bar(It.IsAny<bool>()))
    .Callback<bool>(b => Console.WriteLine("Bar called with: " + b))
    .Returns(42);

var ret = mock.Object.Bar(true);
Console.WriteLine("Result: " + ret);

// output:
// Bar called with: True
// Result: 42

我最近遇到了一个有趣的用例。假设您希望对模拟进行一些调用,但它们会同时发生。所以你无法知道他们被调用的顺序,但你想知道你预期的呼叫确实发生了(无论顺序如何)。你可以这样做:

var cq = new ConcurrentQueue<bool>();
mock.Setup(f => f.Bar(It.IsAny<bool>())).Callback<bool>(cq.Enqueue);
Parallel.Invoke(() => mock.Object.Bar(true), () => mock.Object.Bar(false));
Console.WriteLine("Invocations: " + String.Join(", ", cq));

// output:
// Invocations: True, False

BTW不会被误导的“Returns之前”和“Returns之后的区别”混淆。仅仅是在评估Returns之后或之前运行自定义代码的技术区别。在调用者的眼中,两者都将在返回值之前运行。实际上,如果方法是void - 返回,您甚至无法调用Returns,但它的工作方式相同。有关详细信息,请参阅https://stackoverflow.com/a/28727099/67824

答案 4 :(得分:1)

除了这里的其他好答案之外,我在抛出异常之前用它来执行逻辑。例如,我需要存储传递给方法以供稍后验证的所有对象,以及抛出异常所需的方法(在某些测试用例中)。在.Throws(...)上呼叫Mock.Setup(...)会覆盖Callback()操作,并且永远不会调用它。但是,通过在回调中抛出异常,你仍然可以完成回调必须提供的所有好东西,并且仍然会抛出异常。