从 Func<Task<T>> 获取结果

Getting result from Func<Task<T>>

正在测试的方法

protected override async Task<Name> DoExecuteAsync(NameContext context)
{
    context.ThrowIfNull("context");
    var request = new Request
                      {
                          Id = context.Id,
                          Principal = context.UserPrincipal,
                      };
        return await this.repository.NameAsync(request, new CancellationToken(), context.ControllerContext.CreateLoggingContext());
    }

    protected override Name HandleError(NameContext viewContext, Exception exception)
    {
    if (this.errorSignaller != null)
    {
    this.errorSignaller.SignalFromCurrentContext(exception);
    }

    return Name.Unknown;
} 

这是

的实现
public abstract class BaseQueryAsync<TInput, TOutput> : IQueryAsync<TInput, TOutput>
{
    public async Task<TOutput> ExecuteAsync(TInput context)
    {
        try
        {
            return await this.DoExecuteAsync(context);
        }
        catch (Exception e)
        {
            return this.HandleError(context, e);
        }
    }

    protected abstract Task<TOutput> DoExecuteAsync(TInput context);    

    protected virtual TOutput HandleError(TInput viewContext, Exception exception)
    {    
        ExceptionDispatchInfo.Capture(exception).Throw();
    }
}

测试用例如下

[SetUp]
public void Setup()
{
    var httpContext = MvcMockHelpers.MockHttpContext(isAuthenticated: true);
        this.controller = new Mock<Controller>();
    this.controller.Object.SetMockControllerContext(httpContext.Object);
    this.repoMock = new Mock<IRepository>();
    this.errorSignaller = new Mock<IErrorSignaller>();
    this.query = new NameQuery(this.repoMock.Object, this.errorSignaller.Object);
    this.userPrinciple = new Mock<IPrincipal>();
    this.context = new NameContext(this.controller.Object.ControllerContext, this.userPrinciple.Object);
}

[Test]
public async Task TestDoExecuteAsyncWhenRepositoryFails()
{
    // Arrange
    this.repoMock.Setup(
    x => x.NameAsync(
    It.IsAny<Request>(),
    It.IsAny<CancellationToken>(),
    It.IsAny<ILoggingContext>())).Throws<Exception>();

    // Act
    Func<Task<Name>> act = async () => await this.query.ExecuteAsync(this.context);

    // Assert
    act.ShouldNotThrow();
    this.errorSignaller.Verify(s => s.SignalFromCurrentContext(It.IsAny<Exception>()), Times.Once);
}

为了验证名称对象,当我在

行前使用 var result = await act()
this.errorSignaller.Verify(s => s.SignalFromCurrentContext(It.IsAny<Exception>()), Times.Once);

this.errorSignaller.Verify 失败,因为它的计数是 2 而不是 1。我的目的是检查 Name 对象以及下面的代码。

act.ShouldNotThrow();
this.errorSignaller.Verify(s => s.SignalFromCurrentContext(It.IsAny<Exception>()), Times.Once);

我知道如果我写一个新的测试用例我可以很容易地验证它,但是我有什么办法可以在这个测试中一起做吗?

如果你想测试结果然后使用:

名称结果=等待this.query.ExecuteAsync(this.context);

result.Should().Be(expectefResult);

确保你的测试方法public异步任务

您可以尝试用

查看
await query.ExecuteAsync(this.context);

this.query.ExecuteAsync(this.context).GetAwaiter().GetResult();

如果是 Func:

act.Invoke().GetAwaiter().GetResult();

更新

为了能够验证名称,您需要在函数中进行设置。

//...code removed for brevity
Name expectedName = Name.Unknown;
Name actualName = null;

// Act
Func<Task> act = async () => {
    actualName = await this.query.ExecuteAsync(this.context);
};

// Assert
act.ShouldNotThrow();
actualName
    .Should().NotBeNull()
    .And.Be(expectedName);
//...rest of code

原创

正如评论中已经提到的,act 是 returns 一个 Task.

的函数

虽然等待其实现,但仍需要调用函数本身。由于函数 returns 是一个任务,因此也需要等待它。

Func<Task<Name>> act = async () => await this.query.ExecuteAsync(this.context);
var name = await act();

相当于有下面的功能

async Task<Name> act() {
    return await this.query.ExecuteAsync(this.context);
}

您将不得不以同样的方式等待它

var name = await act();

唯一的区别是前一个示例在委托中具有函数。

尽量避免将 .Result 之类的阻塞调用与 async/await 代码混合使用。这往往会导致死锁。