【问题标题】:xUnit and Moq do not support async - await keywordsxUnit 和 Moq 不支持 async - await 关键字
【发布时间】:2012-06-29 03:25:00
【问题描述】:

我正在尝试发现如何将 async 和 await 关键字应用于我的 xUnit 测试。我正在使用 xUnit 1.9 和 Async CTP 1.3。这是我的测试用例

我有一个指定一个异步方法调用的接口

public interface IDoStuffAsync
{
    Task AnAsyncMethod(string value);
}

我有一个使用接口并调用异步方法的类

public class UseAnAsyncThing
{
    private readonly IDoStuffAsync _doStuffAsync;

    public UseAnAsyncThing(IDoStuffAsync doStuffAsync)
    {
        _doStuffAsync = doStuffAsync;
    }

    public async Task DoThatAsyncOperation(string theValue)
    {
        await _doStuffAsync.AnAsyncMethod(theValue);
    }
}

在我的测试中,我希望检查方法 DoThatAsyncOperation 是否正在调用具有正确值的方法,因此我模拟了接口并使用 Moq 来验证调用

    [Fact]
    public async void The_test_will_pass_even_though_it_should_fail()
    {
        var mock = new Mock<IDoStuffAsync>();
        var sut = new UseAnAsyncThing(mock.Object);

        mock.Setup(x => x.AnAsyncMethod(It.IsAny<string>()));

        await sut.DoThatAsyncOperation("test");

        // This won't throw a Moq.MockExcpetion so the test appears to pass
        // However it does not run
        mock.Verify(x => x.AnAsyncMethod("fail"));
    }

此测试使用asyncawait 关键字。当它运行时,它会错误地通过,因为 Moq 应该断言验证失败。调用sut.DoThatAsyncOperation("test"); 后的任何代码都不会运行

    [Fact]
    public void This_will_work_and_assert_the_reslt()
    {
        var mock = new Mock<IDoStuffAsync>();
        var sut = new UseAnAsyncThing(mock.Object);

        mock.Setup(x => x.AnAsyncMethod(It.IsAny<string>()));

        sut.DoThatAsyncOperation("test").ContinueWith(y => { });

        // This won't throw a Moq.MockExcpetion so the test appears to pass
        // However it does not run
        mock.Verify(x => x.AnAsyncMethod("fail"));
    }

这个测试是在没有 await 和 async 关键字的情况下设置的,并且可以通过。

这是 xUnit 和 Moq 的预期行为吗?


更新

感谢斯蒂芬的评论,我设法通过进行两项更改来修复第一个测试。测试现在返回一个 Task 而不是 void,Mock 也返回一个 Task。

    [Fact]
    public async Task The_test_will_pass_even_though_it_should_fail()
    {
        var mock = new Mock<IDoStuffAsync>();
        var sut = new UseAnAsyncThing(mock.Object);

        mock.Setup(x => x.AnAsyncMethod(It.IsAny<string>())).ReturnAsync(true);

        await sut.DoThatAsyncOperation("test");

        // This now fails as it should
        mock.Verify(x => x.AnAsyncMethod("fail"));
    }

【问题讨论】:

  • 感谢上面的更新段落。 TaskCompletionSource 是我在使用 MOQ 模拟我的异步方法时寻找的关键。
  • 您可以简单地使用 Task.FromResult:mock.Setup(x =&gt; x.AnAsyncMethod(It.IsAny&lt;string&gt;())).Returns(Task.FromResult(true));,而不是显式创建 TaskCompletionSource
  • 你也可以使用 mock.Setup(x => x.AnAsyncMethod(It.IsAny())).ReturnAsync(true);避免设置任务

标签: c# moq async-await xunit.net


【解决方案1】:

将您的单元测试方法更改为返回Task 而不是void,它应该可以工作。支持async void 单元测试is being considered for a future release

我在我的博客上详细描述了why async unit tests don't work by default。 (我的博客示例使用 MSTest,但所有其他测试运行程序都存在相同的问题,包括 xUnit pre-1.9)。

【讨论】:

  • 感谢您的提示,但这也失败了。这次是在运行模拟方法时抛出 NullRefException。我猜这个模拟必须返回一个任务。
  • 事实上,这让我找到了答案。我现在可以通过从模拟返回 TaskCompletionSource 来通过测试。
  • 您也可以使用Task.FromResult 作为同一事物的简写。
【解决方案2】:

我尝试使用您的“更新”中的代码,但它在我正在模拟的异步方法处停止。

    var tcs = new TaskCompletionSource<T>();
    tcs.SetResult(default(T));


    mock.Setup(x => x.AnAsyncMethod(It.IsAny<T>())).Returns(tcs.Task);

所以要解决这个问题,我必须更改“返回”方法:

    mock.Setup(x => x.AnAsyncMethod(It.IsAny<T>())).Returns(()=> { return tcs.Task; } );

【讨论】:

    猜你喜欢
    • 2012-11-28
    • 2016-12-30
    • 1970-01-01
    • 2015-05-04
    • 1970-01-01
    • 2020-07-12
    • 1970-01-01
    • 2017-08-31
    • 1970-01-01
    相关资源
    最近更新 更多