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

ThrowsConstraint

ThrowsConstraint is used to test the exception thrown by a delegate by applying a constraint to it.
using NUnit.Framework.Internal; using System; 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)) try { invocationDescriptor.Invoke(); return null; } catch (Exception result) { return result; } using (AsyncInvocationRegion asyncInvocationRegion = AsyncInvocationRegion.Create(invocationDescriptor.Delegate)) { object invocationResult = invocationDescriptor.Invoke(); try { asyncInvocationRegion.WaitForPendingOperationsToComplete(invocationResult); 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) throw new ArgumentException($"""{actual.GetType().Name}", "actual"); invocationDescriptor = new VoidInvocationDescriptor(testDelegate); } return invocationDescriptor; } } private interface IInvocationDescriptor { Delegate Delegate { get; } object Invoke(); } 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 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 => 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) ? baseConstraint.ApplyTo(caughtException) : null); } public override ConstraintResult ApplyTo<TActual>(ActualValueDelegate<TActual> del) { return ApplyTo(new GenericInvocationDescriptor<TActual>(del)); } } }