【问题标题】:Async lambda expressions in Xunit Assert.ThrowsXunit Assert.Throws 中的异步 lambda 表达式
【发布时间】:2017-06-14 14:30:53
【问题描述】:

我有一些测试代码断言无法通过我的UserRepository 创建重复的Users。

用户.cs:

public class User
{
    public int Id { get; set; }

    public string AccountAlias { get; set; }

    public string DisplayName { get; set; }

    public string Email { get; set; }

    public bool IsActive { get; set; }
}

UserRepository.cs:

public class UserRepository
{
    public virtual async Task<User> CreateAsync(User entity)
    {
        if (entity == null)
        {
            throw new ArgumentNullException("entity");
        }

        if (await GetDuplicateAsync(entity) != null)
        {
            throw new InvalidOperationException("This user already exists");
        }

        return Create(entity);
    }

    public async Task<User> GetDuplicateAsync(User user)
    {
        if (user == null)
        {
            throw new ArgumentNullException("user");
        }

        return await (from u in Users
                      where u.AccountAlias == user.AccountAlias && 
                            u.Id != user.Id && 
                            u.IsActive
                      select u).FirstOrDefaultAsync();
    }
}

UserRepositoryTests.cs:

public sealed class UserRepositoryTests : IDisposable
{
    public UserRepositoryTests()
    {
        UserRepository = new UserRepository(new FooEntities()); // DbContext 
                                                                // from EF
    }

    private UserRepository UserRepository { get; set; }

    [Fact]
    public void DuplicateUserCannotBeCreated()
    {
        var testUser = new User    // This test user already exists in database
        {
            Id = 0,
            AccountAlias = "domain\\foo",
            DisplayName = "Foo",
            Email = "foo@bar.com",
            IsActive = true
        };
        Assert.Throws<InvalidOperationException>(async () => 
            await UserRepository.CreateAsync(testUser));
    }

    public void Dispose()
    {
        if (UserRepository != null)
        {
            UserRepository.Dispose();
        }
    }
}

当我运行这个单元测试时,Xunit.Sdk.ThrowsException 被抛出(即我的InvalidOperationException抛出):

Assert.Throws() 失败 预期:System.InvalidOperationException 实际:(没有抛出异常)

在调试器中,GetDuplicateAsync() 被评估,但在执行 LINQ 查询时,从未返回结果,因此没有引发异常。有人可以帮忙吗?

【问题讨论】:

标签: c# async-await xunit.net


【解决方案1】:

xUnit 的Assert.Throws(至少在版本 1.9.2 上)不支持异步。这已在版本 2 中得到修复,现在有一个 Assert.ThrowsAsync 方法。

因此,您可以升级到 xUnit 2 或创建自己的方法来使其正常工作:

public async static Task<T> ThrowsAsync<T>(Func<Task> testCode) where T : Exception
{
    try
    {
        await testCode();
        Assert.Throws<T>(() => { }); // Use xUnit's default behavior.
    }
    catch (T exception)
    {
        return exception;
    }
    return null;
}

await ThrowsAsync<InvalidOperationException>(async () => await UserRepository.CreateAsync(testUser));

来自Haacked's gist

【讨论】:

  • 我很确定我看到 xUnit 团队提到版本 2.x 将是 async-aware。
  • @ErikSchierboom 你说得对,我只是看了他们的源代码并更新了我的帖子。
  • 这是一个不错的解决方法。它与 Xunit 中的语义稍有不同,因为它还将捕获 T 中的所有派生异常,而 Xunit 仅捕获 T 而不是派生类型。
【解决方案2】:

XUnit 现在默认处理 Assert.ThrowAsync

【讨论】:

    【解决方案3】:

    这对我有用:

    Assert.Throws<AbpValidationException>(() => _personAppService.CreatePersonAsync(new CreatePersonInput { Name = null }));
    

    只是不要使用异步/等待。

    【讨论】:

    • 只有在 CreatePersonAsync 实际上不是异步的情况下才会发生这种情况;如果它的实现在调用时抛出异常,而不是返回代表异常的Task。在实际情况下,后者很可能是实际发生的情况,因此需要对其进行测试。仅当实际实现实际上具有前一种行为很重要时,这只是一种选择。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-11
    • 1970-01-01
    • 2019-02-22
    • 1970-01-01
    • 2017-10-10
    • 2017-12-31
    相关资源
    最近更新 更多