<PackageReference Include="NUnit" Version="3.7.1" />

ThrowsConstraint

ThrowsConstraint is used to test the exception thrown by a delegate by applying a constraint to it.
using NUnit.Framework.Internal; using System; using System.Threading.Tasks; namespace NUnit.Framework.Constraints { public class ThrowsConstraint : PrefixConstraint { private class ThrowsConstraintResult : ConstraintResult { private readonly ConstraintResult baseResult; public ThrowsConstraintResult(ThrowsConstraint constraint, Exception caughtException, ConstraintResult baseResult) : base(constraint, caughtException) { if (caughtException != null && baseResult.IsSuccess) base.Status = ConstraintStatus.Success; else base.Status = ConstraintStatus.Failure; this.baseResult = baseResult; } public override void WriteActualValueTo(MessageWriter writer) { if (base.ActualValue == null) writer.Write("no exception thrown"); else baseResult.WriteActualValueTo(writer); } } internal class ExceptionInterceptor { private ExceptionInterceptor() { } internal static Exception Intercept(object invocation) { IInvocationDescriptor invocationDescriptor = GetInvocationDescriptor(invocation); if (AsyncInvocationRegion.IsAsyncOperation(invocationDescriptor.Delegate)) { using (AsyncInvocationRegion asyncInvocationRegion = AsyncInvocationRegion.Create(invocationDescriptor.Delegate)) { try { object invocationResult = invocationDescriptor.Invoke(); asyncInvocationRegion.WaitForPendingOperationsToComplete(invocationResult); return null; } catch (Exception result) { return result; } } } using (new TestExecutionContext.IsolatedContext()) try { invocationDescriptor.Invoke(); return null; } catch (Exception result2) { return result2; } } private static IInvocationDescriptor GetInvocationDescriptor(object actual) { IInvocationDescriptor invocationDescriptor = actual as IInvocationDescriptor; if (invocationDescriptor == null) { TestDelegate testDelegate = actual as TestDelegate; if (testDelegate != null) invocationDescriptor = new VoidInvocationDescriptor(testDelegate); else { AsyncTestDelegate asyncTestDelegate = actual as AsyncTestDelegate; if (asyncTestDelegate != null) invocationDescriptor = new GenericInvocationDescriptor<Task>(() => asyncTestDelegate()); } } if (invocationDescriptor == null) throw new ArgumentException($"""{actual.GetType().Name}", "actual"); return invocationDescriptor; } } internal class GenericInvocationDescriptor<T> : IInvocationDescriptor { private readonly ActualValueDelegate<T> _del; public Delegate Delegate => _del; public GenericInvocationDescriptor(ActualValueDelegate<T> del) { _del = del; } public object Invoke() { return _del(); } } private interface IInvocationDescriptor { Delegate Delegate { get; } object Invoke(); } private class VoidInvocationDescriptor : IInvocationDescriptor { private readonly TestDelegate _del; public Delegate Delegate => _del; public VoidInvocationDescriptor(TestDelegate del) { _del = del; } public object Invoke() { _del(); return null; } } private Exception caughtException; public Exception ActualException => caughtException; public override string Description => base.BaseConstraint.Description; public ThrowsConstraint(IConstraint baseConstraint) : base(baseConstraint) { } public override ConstraintResult ApplyTo<TActual>(TActual actual) { caughtException = ExceptionInterceptor.Intercept(actual); return new ThrowsConstraintResult(this, caughtException, (caughtException != null) ? base.BaseConstraint.ApplyTo(caughtException) : null); } public override ConstraintResult ApplyTo<TActual>(ActualValueDelegate<TActual> del) { return ApplyTo(new GenericInvocationDescriptor<TActual>(del)); } } }