Xunit中的Assert.Throws异步Lambda表达式

7
我可以帮您翻译成中文,以下是翻译的结果:

我有一些测试代码,用于断言通过我的UserRepository无法创建重复的User

User.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 查询被执行时,结果从未返回,因此没有异常被抛出。能否有人帮忙?

你使用的是哪个XUnit版本? - Wim Coenen
1
@StephenCleary 得到您的允许后,我将更新您在该链接中的答案,指出xUnit 2已解决了这个问题。 - dcastro
@Wim 我正在使用 xUnit 1.9.2。 @dcastro 是对的,我更新了我的 xUnit,问题就解决了。 - rexcfnghk
3个回答

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

来自哈克的代码片段.


我相当确定我看到xUnit团队提到2.x版本将支持“async”。 - Erik Schierboom
@ErikSchierboom 你说得对,我刚刚查看了他们的源代码并更新了我的帖子。 - dcastro
1
这只是一个可以接受的解决方法。它与Xunit中的语义略有不同,因为它还会捕获T的所有派生异常,而Xunit仅捕获T而不包括派生类型。 - Esben Skov Pedersen

0

XUnit现在默认处理Assert.ThrowAsync


-1
这对我有效:
Assert.Throws<AbpValidationException>(() => _personAppService.CreatePersonAsync(new CreatePersonInput { Name = null }));

只是不要使用async/await。


1
只有当CreatePersonAsync实际上不是异步的时候,才会发生这种情况;如果它的实现在调用时抛出异常,而不是返回代表异常的Task。在实际情况中,后者很可能是实际发生的情况,因此需要进行测试。只有在真正的实现实际上具有前面的行为非常重要时,才会采取这种选择。 - Servy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接