你能帮我理解Moq Callback吗?

使用Moq,看着Callback但我还没有find一个简单的例子来理解如何使用它。

你有一个小的工作片段,清楚地说明如何以及何时使用它?

很难打败https://github.com/Moq/moq4/wiki/Quickstart

如果这还不够清楚,我会称之为文档错误…

编辑:为了回应你的澄清…

对于您执行的每个模拟方法Setup ,您都可以指定如下内容:

  • 对投入的限制
  • 返回值(如果有的话)的/方法的值将被导出

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

一般来说,你不会经常需要这样的机制(xUnittesting模式有条件逻辑testing的反模式),如果有更简单的或者内build的方法来确定你所需要的,它应该是优先使用。

Justin Etheredge的Moq系列中的第3部分包含了它, 这里还有另一个callback的例子

以下是使用callback来testing发送到处理插入的数据服务的实体的示例。

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

然后你可以testing类似的东西

 Assert.AreEqual("test", insertedEntity.Description, "Wrong Description"); 

在moq中有两种types的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}"); }); 

在这两个callback中,我们可以:

  1. 检查方法参数
  2. 捕获方法的争论
  3. 改变上下文状态

除了这里的其他好的答案之外,我在抛出exception之前使用它来执行逻辑。 例如,我需要存储传递给方法的所有对象以供后续validation,并且需要抛出exception的方法(在某些testing用例中)。 在.Throws(...)上调用.Throws(...)覆盖Callback()操作,并且永远不会调用它。 但是,通过在callback中抛出exception,您仍然可以执行callback所提供的所有优点,并且仍然会抛出exception。

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 

我最近遇到了一个有趣的用例。 假设你期望有一些调用你的模拟,但他们同时发生。 所以你无法知道他们被调用的顺序,但是你想知道你预期发生的调用(不pipe顺序如何)。 你可以做这样的事情:

 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 

顺便说一句,不要误导“ Returns前”和“ Returns后”的区别。 这仅仅是您的自定义代码是否在评估Returns之后运行的技术区别。 在调用者眼中,两者都会在返回值之前运行。 事实上,如果这个方法是void那么你甚至不能调用Returns ,但它的工作原理是一样的。 有关更多信息,请参阅https://stackoverflow.com/a/28727099/67824