NUnit Cheat Sheet

A cheat sheet for the NUnit test framework

All source code can be found on GitHub here.

This post is part of my cheat sheet series.

 

using System;
using System.Reflection;
using System.IO;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using NUnit.Framework;

namespace NUnitCheatSheet
{
	/// <summary>
	/// TestFixtureSetUp: Provides a config class for setting up and tearing down the test fixture.
	/// Provides the config for all TestFixture classes witin the same namespace.
	/// </summary>
	[SetUpFixture]
  	public class TestFixtureSetUp
  	{
		/// <summary>
		/// SetUp; run once before all tests in a test fixture.
		/// Run once for each TestFixture in the same namespace.
		/// </summary>
	    [SetUp]
		public void RunBeforeAnyTests()
		{
		  // Set up code here.
		}
		/// <summary>
		/// TearDown; run once after all tests in a test fixture.
		/// Run once for each TestFixture in the same namespace.
		/// </summary>
	    [TearDown]
		public void RunAfterAnyTests()
		{
		  // Clear up code here.
		}
  	}

	/// <summary>
	/// Factory class for providing test data for tests with the TestCaseSource attribue.
	/// </summary>
	public class TestCaseDataFactory
	{
		/// <summary>
		/// TestCaseSource tests can consume IEnumerable properties which return TestCaseData
		/// </summary>
	  	public static IEnumerable TestCasesDataForTestCaseSourceTest
		{
		    get
		    {
		      yield return new TestCaseData( 1, "1" ).Returns( 2 ); // Defines the test data and the expected return
		      yield return new TestCaseData( 2, "2" ).Returns( 4 );
		      yield return new TestCaseData( 0, "a" )
						.Throws(typeof(ArgumentException)); // Defines the test data and the expected throw exception
		    }
		}
	}

	/// <summary>
	/// TestFixture defines a class of tests
	/// </summary>
	[TestFixture(), Category("MyTests"), Description("NUnut Cheat Sheet!")]
	public class Test
	{
		/// <summary>
		/// TestCaseSource attributes can use static properties to return an array of test data
		/// </summary>
		public static object[] CaseSourceTestData =
		{
		    new object[] { 1, 1.1m, "2.1" },
			new object[] { 2, 2.2m, "4.2" },
			new object[] { 3, 3.3m, "6.3" },
		};

		# region Setup and Tear Down
		/// <summary>
		/// TestFixtureSetUp called once before any tests have been run in the same TestFixture
		/// </summary>
		[TestFixtureSetUp] public void FixtureSetUp()
    	{
			// Set up code here.
		}
		/// <summary>
		/// TestFixtureTearDown called once after all tests have been run in the same TestFixture
		/// </summary>
    	[TestFixtureTearDown] public void FixtureTearDown()
    	{
			// Clear up code here.
		}

		/// <summary>
		/// SetsUp is called once before each Test within the same TestFxiture
		/// </summary>
		[SetUp] public void SetUp()
    	{
			// Set up code here.
			// If this throws an exception no Test in the TestFixture are run.
		}

		/// <summary>
		/// TearsDown is called once after each Test within the same TestFixture.
		/// </summary>
    	[TearDown] public void TearDown()
    	{
			 // Clear up code here.
			// Will not run if no tess are run due to [SetUp] throwing an exception
		}
		# endregion

		/// <summary>
		/// ExpectedException defines what exception and how it is configured
		/// </summary>
		[Test, ExpectedException( typeof(Exception), ExpectedMessage = "Foo", MatchType = MessageMatch.Exact)]
		public void ExpectedExceptionAttributeTest()
		{
			// MessageMatch is an enum of: Contains, Exact, Regex, StartsWith
			throw new Exception("Foo");
		}

		/// <summary>
		/// Explicit defines tests which are only run manualy.
		/// </summary>
		[Test, Explicit("Test has to be run explicitly")]
    	public void ExplicitAttributeTest()
    	{
		}

		/// <summary>
		/// Ignore marks tests which are not run even if run manually
		/// </summary>
		[Test, Ignore("Ignore this test")]
    	public void IgnoreAttribueTest()
    	{
		}

		/// <summary>
		/// MaxTimeAttribute defines the max run time in milliseconds before the test is considered as failed but allowed to complete.
		/// </summary>
		[Test, MaxTimeAttribute(2000)]
		public void MaxtimeAttributeTest()
		{
		}

		/// <summary>
		/// Timeout defines the max run time in milliseconds before the test is automatically failed.
		/// Can be used on TestFixture to set for each contained Test's
		/// </summary>
		[Test, Timeout(200)]
		public void TimeOutTest()
		{
		}

		/// <summary>
		///
		/// </summary>
		[Test, Platform("Mono"), Culture("en-GB")]
		public void PlatformAttributeTest()
  		{
			// Can also provide exclusion culture and platform
			// Test case is ignored if Platform or Cultrue can not be provided
 		}

		/// <summary>t
		/// Property: categorises the test witin the test output XML.
		/// </summary>
		[Test, Property("PropertyName", "Value")]
		public void PropertyAttributeTest()
    	{}

		/// <summary>
		/// Custom Property: categorises the test within the test output XML.
		/// </summary>
		[Test, CustomPropertyAttribute(CustomPropertyValue.One)]
		public void CustomPropertyAttributeTest()
    	{}

		#region Parameterized
		/// <summary>
		/// Combinatorial: The test is run with each combination of Values for each parameter
		/// </summary>

		[Test, Combinatorial]
		public void CombinatorialAttributeTest ([Values(1,2,3)] int a, [Values("a", "b", "c")] string b)
		{
			// Called 9 times with  parameter pairs of: {1,a}, {1,b}, {1,c}, {2,a}, {3,b}....
		}

		/// <summary>
		/// Random: Test can be run with a random value. Random(Min,Max,Count)
		/// </summary>
		[Test]
		public void RandomAttributeTest(
    	[Random(1, 10, 2)] int value)
		{
			// Called 2 times with a random integer between 1 and 10
			Assert.That(value, Is.InRange(1,10));
		}

		/// <summary>
		/// Sequential: Parameters with values are run in sequence
		/// </summary>
		[Test, Sequential]
		public void SequentialAttributeTest(
    	[Values(1,2,3)] int x,
    	[Values("A","B")] string s)
		{
			// Test runs for parameter pairs {1,A}, {2,B}, {3, null}
		}

		/// <summary>
		/// Range: Parameter is run with a sequence of values incremented. Range(Min,Max,Increment).
		/// </summary>
		[Test]
		public void RangeAttributeTest(
    	[Range(0.0, 1, 0.5)] decimal value)
		{
			// Test run for parameters, 0.0, 0.5, 1.0
			Assert.That(value, Is.InRange(0m,1m));
		}

		/// <summary>
		/// TestCaseSource: referencing a public property which privides a sequence of test data
		/// </summary>
		[Test, TestCaseSource("CaseSourceTestData")]
		public void CaseSourceTest(int a, decimal b, string c)
		{
			// Can also specify the class to which the property is found upon.
			Assert.That(a + b, Is.EqualTo(Decimal.Parse(c)));
		}

		/// <summary>
		/// TestCaseSource: referncing a class and property which provides a sequence of test data and expected output.
		/// </summary>
		[Test,TestCaseSource(typeof(TestCaseDataFactory),"TestCasesDataForTestCaseSourceTest")]
		public decimal TestCaseSourceTest(int a, string b)
		{
			int bInt;
			if( !int.TryParse(b, out bInt))
				throw new ArgumentException(string.Format("Can not parse '{0}' to an integer", b), "b");

			return a + bInt;
		}

		/// <summary>
		/// TestCase: provides a test input data and expected result.
		/// </summary>
		[TestCase(1,1, Result=2)]
		[TestCase(2,2, Result=4)]
		[TestCase(3,3, Result=6)]
		public int TestCaseTest(int a, int b)
		{
		  return( a + b);
		}
		#endregion

		/// <summary>
		/// Assertion Based Test
		/// </summary>
		[Test(), Category("Assertion based testing")]
		public void TestAssertions ()
		{

			// Equality
			Assert.AreEqual (true, true);
			Assert.AreNotEqual (true, false);

			// Identity
			Assert.AreSame (string.Empty, string.Empty);
			Assert.AreNotSame (new object (), new object ());
			Assert.Contains (string.Empty, new List<object> (){string.Empty});

			// Condition
			Assert.IsTrue (true);
			Assert.IsFalse (false);
			Assert.IsNull (null);
			Assert.IsNaN (Double.NaN);
			Assert.IsEmpty (new List<object> ());
			Assert.IsEmpty (new List<object> ());
			Assert.IsNotEmpty (new List<object> (){1});
			Assert.IsNotEmpty ("Foo");

			// Comparision
			Assert.Greater (Decimal.MaxValue, Decimal.MinValue);
			Assert.GreaterOrEqual (Decimal.MinValue, Decimal.MinValue);
			Assert.Less (Decimal.MinValue, Decimal.MaxValue);
			Assert.LessOrEqual (Decimal.MinValue, Decimal.MinValue);

			// Types
			Assert.IsInstanceOf<decimal> (decimal.MinValue);
			Assert.IsNotInstanceOf<int> (decimal.MinValue);
			Assert.IsNotAssignableFrom (typeof(List<Type>), string.Empty);        	// Equivalent to Type.IsAssignableFrom Method
			Assert.IsAssignableFrom (typeof(List<decimal>), new List<decimal> ()); 	// http://msdn.microsoft.com/en-gb/library/system.type.isassignablefrom.aspx
			Assert.IsNotAssignableFrom<List<Type>> (string.Empty);
			Assert.IsAssignableFrom<List<decimal>> (new List<decimal> ());

			// Exceptions
			Assert.Throws (typeof(ArgumentNullException), () => {
				throw new ArgumentNullException (); }
			);

			Assert.Throws<ArgumentNullException> (() => {
				throw new ArgumentNullException (); }
			);

			Assert.DoesNotThrow (() => { });

			Assert.Catch (typeof(Exception), () => {
				throw new ArgumentNullException (); }
			);

			Assert.Catch<Exception> (() => { // Similar as Throws but allows any derrived class of the thrown exception
				throw new ArgumentNullException (); }
			);

			// Strings
			StringAssert.Contains ("Foo", "MooFooMoo");
			StringAssert.StartsWith ("Foo", "FooMoo");
			StringAssert.EndsWith ("Moo", "FooMoo");
			StringAssert.AreEqualIgnoringCase ("FOO", "foo");
			StringAssert.IsMatch ("[0-9]", "1");

			// Collections
			CollectionAssert.AllItemsAreInstancesOfType (new List<decimal> (){ 0m }, typeof(decimal));
			CollectionAssert.AllItemsAreNotNull (new List<decimal> (){ 0m });
			CollectionAssert.AllItemsAreUnique (new List<decimal> (){ 0m, 1m });
			CollectionAssert.AreEqual (new List<decimal> (){ 0m }, new List<decimal> (){ 0m });
			CollectionAssert.AreEquivalent (new List<decimal> (){ 0m, 1m }, new List<decimal> (){ 1m, 0m }); // Same as AreEqual though order does not mater
			CollectionAssert.AreNotEqual (new List<decimal> (){ 0m }, new List<decimal> (){ 1m });
			CollectionAssert.AreNotEquivalent (new List<decimal> (){ 0m, 1m }, new List<decimal> (){ 1m, 2m });  // Same as AreNotEqual though order does not matter
			CollectionAssert.Contains (new List<decimal> (){ 0m, 1m }, 1m);
			CollectionAssert.DoesNotContain (new List<decimal> (){ 0m, 1m }, 2m);
			CollectionAssert.IsSubsetOf (new List<decimal> (){ 1m }, new List<decimal> (){ 0m, 1m }); // {1} is considered a SubSet of {1,2}
			CollectionAssert.IsNotSubsetOf (new List<decimal> (){ 1m, 2m }, new List<decimal> (){ 0m, 1m });
			CollectionAssert.IsEmpty (new List<decimal> (){ });
			CollectionAssert.IsNotEmpty (new List<decimal> (){ 1m });
			CollectionAssert.IsOrdered (new List<decimal> (){ 1m, 2m, 3m });
			CollectionAssert.IsOrdered (new List<string> (){ "a", "A", "b"}, StringComparer.CurrentCultureIgnoreCase);
			CollectionAssert.IsOrdered (new List<int> (){ 3,2,1}, new ReverseComparer()); // Only supports ICompare and not ICompare<T> as of version 2.6

			//  File Assert: various ways to compare a stream or file.
			FileAssert.AreEqual (new MemoryStream (), new MemoryStream ());
			FileAssert.AreEqual (new FileInfo ("MyFile.txt"), new FileInfo ("MyFile.txt"));
			FileAssert.AreEqual ("MyFile.txt", "MyFile.txt");
			FileAssert.AreNotEqual (new FileInfo ("MyFile.txt"), new FileInfo ("MyFile2.txt"));
			FileAssert.AreNotEqual ("MyFile.txt", "MyFile2.txt");
			FileAssert.AreNotEqual (new FileStream ("MyFile.txt", FileMode.Open), new FileStream ("MyFile2.txt", FileMode.Open));

			// Utilities: will cause the test to stop immediatly and mark the test as:
			Assert.Pass ();
			Assert.Fail ();
			Assert.Ignore ();
			Assert.Inconclusive ();

			// Defining the failed message
			Assert.IsTrue(true, "A failed message here"); // also object[] params can be defined
		}

		/// <summary>
		/// Constraint based testing: Test cases with a fluent design pattern
		/// </summary>
		[Test()]
		[Category("Constraint based testing")]
		public void TestConstraints ()
		{

			// Numerical Equality
			Assert.That (1, Is.EqualTo (1));
			Assert.That (1, Is.Not.EqualTo (2));
			Assert.That (1.1, Is.EqualTo (1.2).Within (0.1)); // Allow a tollerance
			Assert.That (1.1, Is.EqualTo (1.01).Within (10).Percent); // Pass tollerance within percent

			// Float Equality
			Assert.That (20000000000000004.0, Is.EqualTo (20000000000000000.0).Within (1).Ulps); // Pass tollerance with units of last place

			// String Equality
			Assert.That ("Foo!", Is.EqualTo ("Foo!"));
			Assert.That ("Foo!", Is.Not.EqualTo ("FOO!"));
			Assert.That ("Foo!", Is.EqualTo ("FOO!").IgnoreCase);
			Assert.That (new List<string> (){ "FOO!"}, Is.EqualTo (new List<string> (){ "Foo!"}).IgnoreCase);

			// Date, Time and TimeSpan equality
			Assert.That (DateTime.Today, Is.EqualTo (DateTime.Today));
			Assert.That (DateTime.Now, Is.Not.EqualTo (DateTime.Now));
			Assert.That (DateTime.Now, Is.EqualTo (DateTime.Now).Within (TimeSpan.FromSeconds (1))); // Time based pass tollerances
			Assert.That (DateTime.Now, Is.EqualTo (DateTime.Now).Within (1).Days);
			Assert.That (DateTime.Now, Is.EqualTo (DateTime.Now).Within (1).Hours);
			Assert.That (DateTime.Now, Is.EqualTo (DateTime.Now).Within (1).Minutes);
			Assert.That (DateTime.Now, Is.EqualTo (DateTime.Now).Within (1).Seconds);
			Assert.That (DateTime.Now, Is.EqualTo (DateTime.Now).Within (1).Milliseconds);

			// Collection equality
			Assert.That (new double[] {1.0,2.0,3.0}, Is.EqualTo (new int[] {1,2,3}));
			Assert.That (new double[] {1.0,2.0,3.0}, Is.Not.EqualTo (new int[] {1,2,3, 4}));
			Assert.That (new double[] {1.0,2.0,3.0,4.0}, Is.EqualTo (new  int[,] {{1,2}, {3,4}}).AsCollection); // Compare data and not collection (flattens a collection of collections)

			// Customer Comparer
			Assert.That( int.MinValue, Is.EqualTo(int.MaxValue).Using(new WhoCaresComparer()));

			// Identity (Same instance of)
			Assert.That( string.Empty, Is.SameAs(string.Empty));
			Assert.That( new object(), Is.Not.SameAs(new object()));

			// Condition
			Assert.That( string.Empty, Is.Not.Null);
			Assert.That( true, Is.True);
			Assert.That( true, Is.Not.False);
			Assert.That( double.NaN, Is.NaN);
			Assert.That( string.Empty, Is.Empty);
			Assert.That( new List<int>(), Is.Empty);
			Assert.That( new List<int>(){1,2,3}, Is.Unique);

			// Comparision
			Assert.That(1, Is.LessThan(2));
			Assert.That(1, Is.GreaterThan(0));
			Assert.That(1, Is.LessThanOrEqualTo(1));
			Assert.That(1, Is.GreaterThanOrEqualTo(1));
			Assert.That(1, Is.AtLeast(0));	// Same as GreaterThanOrEqualTo
			Assert.That(1, Is.AtMost(2)); 	// Same as LessThanOrEqualTo
			Assert.That(1, Is.InRange(1,2));

			// Path: comparision on file path only without comparing the contents.
			// All paths are converted to 'canonical' path before comparing; full direct path to file.
			Assert.That( "MyFile.txt", Is.SamePath("MyFile.txt"));
			Assert.That( "MyFile.txt", Is.SamePath( "MYFILE.TXT" ).IgnoreCase );
			Assert.That( "MyFile.txt", Is.SamePath("MyFile.txt").RespectCase);
			Assert.That( "/usr/bin", Is.SubPath("/usr"));			// directory exists within another
			Assert.That( "/usr/bin", Is.SamePathOrUnder("/usr"));	// SamePath or SubPath

			// Type Constraints
			Assert.That(new MemoryStream(), Is.InstanceOf(typeof(Stream)));  // Is type or ancestor
			Assert.That(new MemoryStream(), Is.TypeOf(typeof(MemoryStream))); // Is type and not ancestor
			Assert.That(new object(), Is.AssignableFrom(typeof(MemoryStream))); // Can be assigned from
			Assert.That(new MemoryStream(), Is.AssignableTo(typeof(Stream))); // Can be assignable to

			Assert.That(new MemoryStream(), Is.InstanceOf<Stream>());  // Is type or ancestor
			Assert.That(new MemoryStream(), Is.TypeOf<MemoryStream>()); // Is type and not ancestor
			Assert.That(new object(), Is.AssignableFrom<MemoryStream>()); // Can be assigned from
			Assert.That(new MemoryStream(), Is.AssignableTo<Stream>()); // Can be assignable to

			// String Comparision
			Assert.That( "Foo", Is.StringContaining( "o" ) );
			Assert.That( "Foo", Is.StringContaining( "O" ).IgnoreCase );
			Assert.That( "Foo", Is.StringStarting( "F" ) );
			Assert.That( "Foo", Is.StringEnding( "o" ) );
			Assert.That( "Foo", Is.StringMatching( "^[F]" ) ); // Regular ecpression match

			// Collection Comparison
			Assert.That( new List<int>(){1,2,3}, Has.All.GreaterThan(0) );
			Assert.That( new List<int>(){1,2,3}, Is.All.GreaterThan(0) );
			Assert.That( new List<int>(){1,2,3}, Has.None.GreaterThan(4));
			Assert.That( new List<int>(){1,2,3}, Has.Some.GreaterThan(2));
			Assert.That( new List<int>(){1,2,3}, Has.Count.EqualTo(3));
			Assert.That( new List<int>(){1,2,3}, Is.Unique);
			Assert.That( new List<int>(){1,2,3}, Has.Member(1));  // Contains
			Assert.That( new List<int>(){1,2,3}, Is.EquivalentTo( new List<int>(){3,2,1})); // Same data without carring about the order
			Assert.That( new List<int>(){1,2,}, Is.SubsetOf( new List<int>(){3,2,1})); // All are cotained.

			// Property Constraint
			Assert.That( new List<int>(), Has.Property("Count").EqualTo(0));  // Checks public property
			Assert.That( new List<int>(), Has.Count.EqualTo(0));			  // Same as Has.Property("Count").EqualTo(0)
			Assert.That( string.Empty, Has.Length.EqualTo(0));				  // Same as Has.Property("Length").EqualTo(0)
			Assert.That ( new Exception("Foo"), Has.Message.EqualTo("Foo"));  // Exception has message
			Assert.That ( new Exception("Foo", new ArgumentException("Moo")), // Inner exception is of type and has message
			             Has.InnerException.InstanceOf<ArgumentException>().And.InnerException.Message.EqualTo("Moo"));

			// Throws Constraint
			// See also Property constrains for Message and Inner Exception
			// Throws.Exception allow AND, Throws.InstanceOf<> is short hand
			Assert.That (() => { throw new ArgumentException("Foo"); },
				Throws.Exception.InstanceOf<ArgumentException>().And.Message.EqualTo("Foo"));

			Assert.That (() => { throw new ArgumentNullException("Foo"); },
				Throws.Exception.TypeOf<ArgumentNullException>());

			Assert.That (() => { throw new ArgumentNullException("Foo"); },
				Throws.InstanceOf<ArgumentNullException>());

			Assert.That (() => { }, Throws.Nothing);

			Assert.That (() => { throw new Exception("Foo", new ArgumentException("Moo"));},
				Throws.Exception.Message.EqualTo("Foo").And.InnerException.InstanceOf<ArgumentException>());

			Assert.That (() => { throw new ArgumentException(); }, Throws.ArgumentException);

			// Compound Constraints
			Assert.That( 2, Is.Not.EqualTo( 1 ));
			Assert.That( new List<int>(){ 1, 2, 3 }, Is.All.GreaterThan( 0 ) );

			Assert.That( 1, Is.GreaterThan( 0 ).And.LessThan( 2 ) );  // .And amd & are the same
			Assert.That( 1, Is.GreaterThan( 0 ) & Is.LessThan( 2 ) );

			Assert.That( 1, Is.LessThan( 10 ) | Is.GreaterThan( 0 ) ); // .Or and | are the same
			Assert.That( 1, Is.LessThan( 10 ).Or.GreaterThan( 0 ) );

			// Delayed
			Assert.That(() => { return true;}, Is.True.After(10));		// Passes after x time
			Assert.That(() => { return true;}, Is.True.After(10, 10));	// Passes after x time and polling..

			// List Mapper
			Assert.That(List.Map(new List<string>(){"1", "22", "333"}).Property("Length"),  // Allows all elememts in a list to have a condition provided for them
			            Is.EqualTo(new List<int>(){1,2,3}));
		}
	}
	# region Elements consumed by the tests above
	public class WhoCaresComparer : IEqualityComparer<int>
	{
	    public bool Equals(int b1, int b2)
	    {
			return true;
	    }

	    public int GetHashCode(int aNumber)
	    {
			return aNumber.GetHashCode();
	    }
	}

	public class ReverseComparer : IComparer
	{
		public int Compare (object a, object b)
		{
			return ((int)b).CompareTo((int)a);
		}
	}

	public enum CustomPropertyValue
	{
    	One,
    	Two,
   	}

	[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
	public class CustomPropertyAttribute : PropertyAttribute
	{
    	public CustomPropertyAttribute( CustomPropertyValue value ) : base( "Custom", value.ToString() )
		{
		}
	}
	#endregion
}
Advertisements

2 thoughts on “NUnit Cheat Sheet

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s