从Func获取结果<task <t>&gt;

时间:2017-09-07 12:53:09

标签: c# unit-testing async-await nunit fluent-assertions

待测方法

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

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

3 个答案:

答案 0 :(得分:0)

如果您想测试结果,请使用:

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

result.Should()BE(expectefResult);

确保将您的测试方法设为公共异步任务

答案 1 :(得分:-1)

您可以尝试使用

进行检查
await query.ExecuteAsync(this.context);

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

,如果是Func:

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

答案 2 :(得分:-1)

更新

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

//...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是一个返回Task的函数。

在等待其实现时,仍然需要调用函数本身。由于该函数返回一个Task,因此也需要等待它。

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代码混合使用。这往往会导致死锁。