你能帮我理解Moq回调吗?

时间:2022-03-08 03:09:49

Using Moq and looked at Callback but I have not been able to find a simple example to understand how to use it.

使用Moq并查看回调,但我无法找到一个简单的示例来理解如何使用它。

Do you have a small working snippet which clearly explain how and when to use it?

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

5 个解决方案

#1


64  

Hard to beat https://github.com/Moq/moq4/wiki/Quickstart

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

If that's not clear enough, I'd call that a doc bug...

如果这还不够清楚的话,我把它叫做文档错误……

EDIT: In response to your clarification...

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

For each mocked method Setup you perform, you get to indicate things like:

对于您执行的每一个模拟方法设置,您都要指出如下内容:

  • constraints on inputs
  • 限制输入
  • the value for / way in which the return value (if there is one) is to be derived
  • 要派生返回值(如果有)的值/方法

The .Callback mechanism says "I can't describe it right now, but when a call shaped like this happens, call me back and I'll do what needs to be done". As part of the same fluent call chain, you get to control the result to return (if any) via .Returns". In the QS examples, an example is that they make the value being returned increase each time.

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

In general, you won't need a mechanism like this very often (xUnit Test Patterns have terms for antipatterns of the ilk Conditional Logic In Tests), and if there's any simpler or built-in way to establish what you need, it should be used in preference.

通常,您不会经常需要这样的机制(xUnit测试模式在测试中有关于ilk条件逻辑反模式的术语),并且如果有任何更简单或内置的方法来确定您需要什么,应该优先使用它。

Part 3 of 4 in Justin Etheredge's Moq series covers it, and there's another example of callbacks here

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

#2


45  

Here's an example of using a callback to test an entity sent to a Data Service that handles an insert.

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

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

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

Alternative generic method syntax:

替代通用方法的语法:

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

Then you can test something like

然后你可以测试类似的东西

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

#3


6  

There are two types of Callback in moq. One happens before the call returns; the other happens after the call returns.

在moq中有两种类型的回调。一个在调用返回之前发生;另一个发生在调用返回之后。

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

In both callbacks, we can:

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

  1. inspect method arguments
  2. 检查方法参数
  3. capture method arguemnts
  4. 捕捉方法arguemnts
  5. change contextual state
  6. 改变上下文状态

#4


2  

Callback is simply a means to execute any custom code you want when a call is made to one of the mock's methods. Here's a simple example:

回调只是在调用某个mock方法时执行任何自定义代码的一种方法。这是一个简单的例子:

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

I recently ran into an interesting use case for it. Suppose you expect some calls to your mock, but they happen concurrently. So you have no way of knowing the order in which they'd get called, but you want to know the calls you expected did take place (irrespective of order). You can do something like this:

我最近遇到了一个有趣的用例。假设您期望对mock进行一些调用,但它们同时发生。因此,您无法知道它们被调用的顺序,但是您想知道您所期望的调用是否发生(不考虑顺序)。你可以这样做:

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 don't get confused by the misleading "before Returns" and "after Returns" distinction. It is merely a technical distinction of whether your custom code will run after Returns has been evaluated or before. In the eyes of the caller, both will run before the value is returned. Indeed, if the method is void-returning you can't even call Returns and yet it works the same. For more information see https://*.com/a/28727099/67824.

顺便说一句,不要被误导的“返回前”和“返回后”的区别搞混。它仅仅是一个技术上的区别,即您的自定义代码是在返回被评估之后运行,还是在返回之前运行。在调用者的眼中,两者都将在返回值之前运行。实际上,如果方法是void-return,您甚至不能调用return,但它的工作原理是一样的。有关更多信息,请参见https://*.com/a/28727099/67824。

#5


1  

On top of the other good answers here, I've used it to perform logic before throwing an exception. For instance, I needed to store all objects that were passed to a method for later verification, and that method (in some test cases) needed to throw an exception. Calling .Throws(...) on Mock.Setup(...) overrides the Callback() action and never calls it. However, by throwing an exception within the Callback, you can still do all of the good stuff that a callback has to offer, and still throw an exception.

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

#1


64  

Hard to beat https://github.com/Moq/moq4/wiki/Quickstart

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

If that's not clear enough, I'd call that a doc bug...

如果这还不够清楚的话,我把它叫做文档错误……

EDIT: In response to your clarification...

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

For each mocked method Setup you perform, you get to indicate things like:

对于您执行的每一个模拟方法设置,您都要指出如下内容:

  • constraints on inputs
  • 限制输入
  • the value for / way in which the return value (if there is one) is to be derived
  • 要派生返回值(如果有)的值/方法

The .Callback mechanism says "I can't describe it right now, but when a call shaped like this happens, call me back and I'll do what needs to be done". As part of the same fluent call chain, you get to control the result to return (if any) via .Returns". In the QS examples, an example is that they make the value being returned increase each time.

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

In general, you won't need a mechanism like this very often (xUnit Test Patterns have terms for antipatterns of the ilk Conditional Logic In Tests), and if there's any simpler or built-in way to establish what you need, it should be used in preference.

通常,您不会经常需要这样的机制(xUnit测试模式在测试中有关于ilk条件逻辑反模式的术语),并且如果有任何更简单或内置的方法来确定您需要什么,应该优先使用它。

Part 3 of 4 in Justin Etheredge's Moq series covers it, and there's another example of callbacks here

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

#2


45  

Here's an example of using a callback to test an entity sent to a Data Service that handles an insert.

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

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

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

Alternative generic method syntax:

替代通用方法的语法:

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

Then you can test something like

然后你可以测试类似的东西

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

#3


6  

There are two types of Callback in moq. One happens before the call returns; the other happens after the call returns.

在moq中有两种类型的回调。一个在调用返回之前发生;另一个发生在调用返回之后。

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

In both callbacks, we can:

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

  1. inspect method arguments
  2. 检查方法参数
  3. capture method arguemnts
  4. 捕捉方法arguemnts
  5. change contextual state
  6. 改变上下文状态

#4


2  

Callback is simply a means to execute any custom code you want when a call is made to one of the mock's methods. Here's a simple example:

回调只是在调用某个mock方法时执行任何自定义代码的一种方法。这是一个简单的例子:

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

I recently ran into an interesting use case for it. Suppose you expect some calls to your mock, but they happen concurrently. So you have no way of knowing the order in which they'd get called, but you want to know the calls you expected did take place (irrespective of order). You can do something like this:

我最近遇到了一个有趣的用例。假设您期望对mock进行一些调用,但它们同时发生。因此,您无法知道它们被调用的顺序,但是您想知道您所期望的调用是否发生(不考虑顺序)。你可以这样做:

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 don't get confused by the misleading "before Returns" and "after Returns" distinction. It is merely a technical distinction of whether your custom code will run after Returns has been evaluated or before. In the eyes of the caller, both will run before the value is returned. Indeed, if the method is void-returning you can't even call Returns and yet it works the same. For more information see https://*.com/a/28727099/67824.

顺便说一句,不要被误导的“返回前”和“返回后”的区别搞混。它仅仅是一个技术上的区别,即您的自定义代码是在返回被评估之后运行,还是在返回之前运行。在调用者的眼中,两者都将在返回值之前运行。实际上,如果方法是void-return,您甚至不能调用return,但它的工作原理是一样的。有关更多信息,请参见https://*.com/a/28727099/67824。

#5


1  

On top of the other good answers here, I've used it to perform logic before throwing an exception. For instance, I needed to store all objects that were passed to a method for later verification, and that method (in some test cases) needed to throw an exception. Calling .Throws(...) on Mock.Setup(...) overrides the Callback() action and never calls it. However, by throwing an exception within the Callback, you can still do all of the good stuff that a callback has to offer, and still throw an exception.

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