7

I have some test code asserting duplicate Users cannot be created through my UserRepository.

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

When I run this unit test, Xunit.Sdk.ThrowsException is thrown (i.e. my InvalidOperationException was not thrown):

Assert.Throws() Failure Expected: System.InvalidOperationException Actual: (No exception was thrown)

From the debugger, GetDuplicateAsync() was evaluated but when the LINQ query was executed, the result was never returned and thus no exception was thrown. Can anyone help?

rexcfnghk
  • 14,435
  • 1
  • 30
  • 57
  • Which XUnit version are you using? – Wim Coenen Mar 17 '14 at 10:16
  • 1
    possible duplicate of [How to handle exceptions thrown by Tasks in xUnit .net's Assert.Throws?](http://stackoverflow.com/questions/14084923/how-to-handle-exceptions-thrown-by-tasks-in-xunit-nets-assert-throwst) – Stephen Cleary Mar 17 '14 at 11:58
  • 1
    @StephenCleary With your permission, I'll update your answer on that link to point out that xUnit 2 has addressed this issue. – dcastro Mar 17 '14 at 14:14
  • @Wim i am using `xUnit` 1.9.2. @dcastro was right, i updated my `xUnit` and the problem was gone. – rexcfnghk Mar 17 '14 at 14:20

3 Answers3

18

xUnit's Assert.Throws (at least on version 1.9.2) is not async-aware. This was fixed in version 2, which now has an Assert.ThrowsAsync method.

So, you can either upgrade to xUnit 2 or create your own method to get it working:

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

From Haacked's gist.

dcastro
  • 66,540
  • 21
  • 145
  • 155
  • I am fairly sure that I saw the xUnit team mention that version 2.x will be `async`-aware. – Erik Schierboom Mar 17 '14 at 10:21
  • @ErikSchierboom You're right, I just took a look at their source code and updated my post. – dcastro Mar 17 '14 at 10:25
  • 1
    This is an ok work around. It just has slightly different semantics than the one in Xunit since this will also catch all derived exceptions from T where Xunit only catches T and not derived types. – Esben Skov Pedersen Oct 23 '15 at 10:13
0

XUnit now handle Assert.ThrowAsync by default

joe
  • 84
  • 2
-1

This works for me:

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

Just don't use async/await.

hikalkan
  • 2,234
  • 15
  • 17
  • 1
    That will only happen if `CreatePersonAsync` isn't actually asynchronous; and if it's implementation is throwing the exception when called, rather than returning a `Task` that represents the exception. In real situations, the latter is likely to be what actually happens, so that's what needs to be tested for. This is only an option if it's important that the real implementation actually has the former behavior. – Servy Jan 30 '15 at 17:12