Search Results for

    Show / Hide Table of Contents

    Async Constraint Support

    NUnit constraints fully support testing asynchronous code. This page covers patterns for using constraints with async methods, tasks, and delegates.

    The examples on this page use this small service and calculator:

    // A small service used by the examples below
    public class SampleService
    {
        private int _readyChecks;
        private readonly List<int> _cache = [];
    
        public Task<int> GetValueAsync() => Task.FromResult(42);
    
        public Task InitializeAsync() => Task.CompletedTask;
    
        public Task SafeMethodAsync() => Task.CompletedTask;
    
        public Task InvalidOperationAsync() => throw new InvalidOperationException("expected error");
    
        public async Task FailingMethodAsync()
        {
            await Task.Yield();
            throw new InvalidOperationException("expected error");
        }
    
        public Task FailingWithInnerExceptionAsync() =>
            Task.FromException(new AggregateException(new TimeoutException()));
    
        // Becomes ready on the third call
        public Task<bool> IsReadyAsync() => Task.FromResult(++_readyChecks >= 3);
    
        public Task<int> GetCountAsync()
        {
            _cache.Add(_cache.Count);
            return Task.FromResult(_cache.Count);
        }
    
        public async IAsyncEnumerable<int> GetItemsAsync()
        {
            foreach (var item in new[] { 50, 150, 250, 350, 450 })
            {
                await Task.Yield();
                yield return item;
            }
        }
    }
    
    public class Calculator
    {
        public Task<int> AddAsync(int a, int b) => Task.FromResult(a + b);
    }
    

    Testing Async Methods

    Basic Async Assertions

    Use async lambdas with Assert.That to test async code:

    [Test]
    public void BasicAsyncAssertions()
    {
        // Test async method return values
        Assert.That(async () => await service.GetValueAsync(), Is.EqualTo(42));
    
        // Test that async method completes without throwing
        Assert.That(async () => await service.InitializeAsync(), Throws.Nothing);
    
        // Test async exception throwing
        Assert.That(async () => await service.InvalidOperationAsync(),
            Throws.TypeOf<InvalidOperationException>());
    }
    

    Testing Task Results

    You can also await tasks directly before asserting:

    [Test]
    public async Task TestingTaskResults()
    {
        // Await first, then assert
        var result = await calculator.AddAsync(2, 3);
        Assert.That(result, Is.EqualTo(5));
    
        // Or use Assert.ThatAsync (NUnit 4.0+)
        await Assert.ThatAsync(async () => await calculator.AddAsync(2, 3), Is.EqualTo(5));
    }
    

    Exception Testing with Async Code

    ThrowsConstraint with Async

    [Test]
    public void ThrowsConstraintWithAsync()
    {
        // Test for specific exception type
        Assert.That(async () => await service.FailingMethodAsync(),
            Throws.TypeOf<InvalidOperationException>());
    
        // Test exception message
        Assert.That(async () => await service.FailingMethodAsync(),
            Throws.TypeOf<InvalidOperationException>()
                .With.Message.Contains("expected error"));
    
        // Test inner exception
        Assert.That(async () => await service.FailingWithInnerExceptionAsync(),
            Throws.TypeOf<AggregateException>()
                .With.InnerException.TypeOf<TimeoutException>());
    }
    

    ThrowsNothing with Async

    [Test]
    public void ThrowsNothingWithAsync()
    {
        // Verify async method completes successfully
        Assert.That(async () => await service.SafeMethodAsync(), Throws.Nothing);
    }
    

    Delayed Constraints with Async

    Use DelayedConstraint for testing eventually-consistent async operations:

    [Test]
    public void DelayedConstraintsWithAsync()
    {
        // Poll until condition is true (or timeout)
        Assert.That(async () => await service.IsReadyAsync(),
            Is.True.After(5).Seconds.PollEvery(100).MilliSeconds);
    
        // Wait for value to appear
        Assert.That(async () => await service.GetCountAsync(),
            Is.GreaterThan(0).After(2000, 100));
    }
    

    Collection Constraints with Async Enumerables

    For IAsyncEnumerable<T>, materialize the collection first. ToListAsync is built into .NET 10; on earlier versions it comes from the System.Linq.Async package:

    [Test]
    public async Task CollectionConstraintsWithAsyncEnumerables()
    {
        // Convert to list first
        var items = await service.GetItemsAsync().ToListAsync();
        Assert.That(items, Has.Count.EqualTo(5));
        Assert.That(items, Is.All.Positive);
    
        // Or inline the conversion
        Assert.That(await service.GetItemsAsync().ToListAsync(), Has.Some.GreaterThan(100));
    }
    

    Best Practices

    1. Use async lambdas when testing methods that return Task or Task<T>:

      Assert.That(async () => await method(), constraint);
      
    2. Avoid blocking calls like .Result or .Wait() - they can cause deadlocks:

      // BAD - can deadlock
      Assert.That(service.GetValueAsync().Result, Is.EqualTo(42));
      
      // GOOD - use async lambda
      Assert.That(async () => await service.GetValueAsync(), Is.EqualTo(42));
      
    3. Use Throws.Nothing to explicitly verify async code doesn't throw:

      Assert.That(async () => await service.ProcessAsync(), Throws.Nothing);
      
    4. Use DelayedConstraint for eventually-consistent operations instead of Task.Delay:

      // BAD - arbitrary wait
      await Task.Delay(1000);
      Assert.That(value, Is.True);
      
      // GOOD - polls until true or timeout
      Assert.That(() => value, Is.True.After(2).Seconds.PollEvery(100).MilliSeconds);
      

    See Also

    • Throws Constraint - Exception testing
    • ThrowsNothing Constraint - Verify no exception
    • Delayed Constraint - Polling and timeouts
    • Edit this page
    In this article
    Back to top Generated by DocFX | Copyright (c) 2018- The NUnit Project - Licensed under CC BY-NC-SA 4.0