You can implement your own custom constraints by creating a class that inherits from the Constraint abstract class, which supports performing a test on an actual value and generating appropriate messages. The class includes two abstract methods, which you must override and four virtual methods with default implementation that may be overridden as needed:
public abstract class Constraint { ... public abstract bool Matches( object actual ); public virtual bool Matches( ActualValueDelegate del ); public virtual bool Matches<T>( ref T actual ); public abstract void WriteDescriptionTo( MessageWriter writer ); public virtual void WriteMessageTo( MessageWriter writer ); public virtual void WriteActualValueTo( MessageWriter writer ); ... }
Your derived class should save the actual argument to Matches in the protected field actual for later use.
The MessageWriter abstract class is implemented in the framework by TextMessageWriter. Examining the source for some of the builtin constraints should give you a good idea of how to use it if you have special formatting requirements for error messages.
Having written a custom constraint class, you can use it directly through its constructor:
Assert.That(myObject, new CustomConstraint());
You may also use it in expressions through NUnit's Matches syntax element:
Assert.That(myObject, Is.Not.Null.And.Matches(new CustomConstraint());
The direct construction approach is not very convenient or easy to read. For its built-in constraints, NUnit includes classes that implement a special constraint syntax, allowing you to write things like...
Assert.That( myArray, Is.All.InRange(1,100) );
Ideally, that's what you would like to do with the custom constraint as well. To accomplish this, two separate steps are required:
Assert.That( myObject, Is.Custom(x,y) );
Assert.That( myList, Is.Not.All.Custom(x,y) );