.net 你能帮助我理解Moq回调吗?

wz3gfoph  于 2022-11-19  发布在  .NET
关注(0)|答案(6)|浏览(133)

使用Moq并查看了Callback,但我一直无法找到一个简单的例子来理解如何使用它。
你有一个小的工作片段,清楚地解释如何和何时使用它?

yquaqz18

yquaqz181#

难以击败https://github.com/Moq/moq4/wiki/Quickstart
如果这还不够清楚的话,我会称之为博士窃听器...
编辑:作为对您澄清的回应...
对于您执行的每个模拟方法Setup,您可以指示如下内容:

  • 输入约束
  • 返回值(如果有)的派生方式/的值

.Callback机制说“我现在不能描述它,但是当一个像这样的调用发生时,给我回电话,我会做需要做的事情”。作为同一个流畅调用链的一部分,你可以通过.Returns控制返回的结果(如果有的话)。在QS示例中,一个例子是它们使返回的值每次都增加。
通常,您不会经常需要这样的机制(xUnit测试模式中有类似测试中条件逻辑的反模式术语),如果有任何更简单或内置的方法来建立您所需要的,应该优先使用它。
Part 3 of 4 in Justin Etheredge's Moq series覆盖它,而there's another example of callbacks here
一个简单的回调示例可以在Using Callbacks with Moq post中找到。

i34xakig

i34xakig2#

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

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");
4xy9mtcn

4xy9mtcn3#

Callback只是一种在调用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

我最近遇到了一个有趣的用例。假设你期望一些对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

顺便说一句,不要被误导性的“before Returns“和“after Returns“的区别所迷惑。这只是一个技术上的区别,即您的自定义代码是在Returns求值之后还是之前运行。在调用者看来,两者都将在返回值之前运行。实际上,如果方法是void-返回,你甚至不能调用Returns,但它的工作原理是一样的。有关更多信息,请参见https://stackoverflow.com/a/28727099/67824

goucqfw6

goucqfw64#

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.检查方法参数
1.撷取方法参数
1.更改上下文状态

jfewjypa

jfewjypa5#

除了这里的其他好答案之外,我还使用它在抛出异常之前执行逻辑。(在某些测试案例中)需要掷回例外状况。在Mock.Setup(...)上呼叫.Throws(...)会覆写Callback()动作,而且永远不会呼叫它。不过,通过在回调中抛出一个异常,您仍然可以完成回调必须提供的所有好的功能,并且仍然抛出一个异常。

hfyxw5xn

hfyxw5xn6#

我再补充一个例子:
我需要测试的方法名为Add,它通过执行另一个方法将结果存储在数据库中,并返回void

public class SystemUnderTest
{
    private readonly Repository _repository;

    public SystemUnderTest(Repository repository)
    {
        _repository = repository;
    }

    public void Add(int a, int b)
    {
        int result = a + b;
        _repository.StoreResult(result);
    }
}

public class Repository
{
    public void StoreResult(int result)
    {
        // stores the result in the database
    }
}

由于Add的返回类型,我不能直接得到结果并Assert它。我必须得到StoreResult方法的输入。为此,我在模拟Repository方法时使用回调。

using Moq;
using Xunit;

namespace TestLocal.Tests;

public class CallbackTest
{
    private readonly SystemUnderTest _sut;
    private readonly Mock<Repository> _repository;

    public CallbackTest()
    {
        _repository = new Mock<Repository>(MockBehavior.Strict);
        _sut = new SystemUnderTest(_repository.Object);

    }

    [Fact]
    public void AddTest()
    {
        int a = 1;
        int b = 2;

        int result = -1;
        _repository.Setup(x => x.StoreResult(3))
            .Callback<int>(callbackResult => result = callbackResult)
            .Verifiable();

        _sut.Add(a,b);

        Assert.Equal(a+b, result);
    }
}

相关问题