你能帮我理解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中,我们可以:
- 检查方法参数
- 捕获方法的争论
- 改变上下文状态
除了这里的其他好的答案之外,我在抛出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 。