Repository: nunit/nunit

Issue #1899: Constraint Throws.Exception does not catch exception with async lambdas
Created: 2016-11-10T13:56:34Z
After converting tests from NUnit 2.6.4 to 3.5.0, a few of them starting failing for no reason. After a quick diagnostic, all of them use the constraint Throws.Exception with an async lambda (don't care about the actual exception type, just checking that it fails on invalid input).
The behavior is that the test case fails by throwing the actual Exception, instead of catching it.
I have other places that use Throws.Exception with regular non-async lambda, and all of them work as intended.
Finally, I noticed that if I replace Throws.Exception with Throws.Exception.TypeOf(...) then the test pass properly with async lambdas.
Simple repro here:
namespace Tests
{
	using System;
	using System.Threading;
	using System.Threading.Tasks;
	using NUnit.Framework;

	[TestFixture]
	public class Test_Throw_Exception
	{

		[Test]
		public void Test_Blocking()
		{
			Assert.That(() => { Thread.Sleep(1000); throw new Exception("KAPOW"); }, Throws.Exception);
			// => PASS
		}

		[Test]
		public async Task Test_Async_1()
		{
			Assert.That(async () => { await Task.Delay(1000); throw new Exception("KAPOW"); }, Throws.Exception);
			// => will throw
		}

		[Test]
		public async Task Test_Async_2()
		{
			Assert.That(async () => { await Task.Delay(1000); throw new Exception("KAPOW"); }, Throws.Exception.TypeOf(typeof(Exception)));
			// => PASS
		}

	}
}
With NUnit 2.6.4, all three test cases pass.
With NUnit 3.5.0, tests Test_Blocking and Test_Async_2 pass, but test Test_Async_1 fails with the following output:
Test 'Tests.Test_Throw_Exception.Test_Async_1' failed: System.Exception : KAPOW
	Project\Test.cs(310,0): at Tests.Test_Throw_Exception.<>c.<<Test_Async_1>b__1_0>d.MoveNext()
	--- End of stack trace from previous location where exception was thrown ---
	at NUnit.Framework.Internal.ExceptionHelper.Rethrow(Exception exception)
	at NUnit.Framework.Internal.AsyncInvocationRegion.AsyncTaskInvocationRegion.WaitForPendingOperationsToComplete(Object invocationResult)
	at NUnit.Framework.Constraints.Constraint.ApplyTo[TActual](ActualValueDelegate`1 del)
	at NUnit.Framework.Assert.That[TActual](ActualValueDelegate`1 del, IResolveConstraint expr, String message, Object[] args)
	at NUnit.Framework.Assert.That[TActual](ActualValueDelegate`1 del, IResolveConstraint expr)
	Project\Test.cs(310,0): at Tests.Test_Throw_Exception.<Test_Async_1>d__1.MoveNext()
	--- End of stack trace from previous location where exception was thrown ---
	at NUnit.Framework.Internal.ExceptionHelper.Rethrow(Exception exception)
	at NUnit.Framework.Internal.AsyncInvocationRegion.AsyncTaskInvocationRegion.WaitForPendingOperationsToComplete(Object invocationResult)
	at NUnit.Framework.Internal.Commands.TestMethodCommand.RunAsyncTestMethod(TestExecutionContext context)

0 passed, 1 failed, 0 skipped, took 1,95 seconds (NUnit 3.5.0).

Tested with Resharper, TestDriven.net and NUnit.ConsoleRunner.3.5.0 package, under VS2015

Duplicate issue #813: ThrowsConstraint behavior inconsistent for async delegates 
Created: 2015-09-17T12:31:58Z
ThrowsConstraint behaves differently depending on whether the exception is thrown directly by the method under test or returned as part of the resulting Task.
[TestFixture]
public class TestFixture
{
  [Test]
  public void TestFails()
  {
    Assert.That(() => ThrowsDirectly(), Throws.TypeOf<InvalidOperationException>());
  }

  [Test]
  public void TestSucceeds()
  {
    Assert.That(() => ThrowsAsync(), Throws.TypeOf<InvalidOperationException>());
  }

  private Task ThrowsDirectly()
  {
    throw new InvalidOperationException();
  }

  private async Task ThrowsAsync()
  {
    await Task.Yield();
    throw new InvalidOperationException();
  }
}

Closing comment (2018-01-13T23:50:26Z): @mikkelbu is correct. #1899 was a duplicate of this issue and we published the fix with release 3.9.0 in November.
I'll close this issue as a duplicate of #1899 even though, in fairness, it should have been the other way around. 😃