Wednesday, November 24, 2010

Zen Testing Extension Methods

After my last Zen Testing presentation, numerous requests have come in to share the unit test extension methods I use. They are attached at the end of this post (this class is currently setup to use MSTest, but it can be easily adapted to use other test frameworks).

The intent for creating these extension methods was to make for more readable assertions in my unit tests. So rather than:

//arrange
string expected = "Testing123";
Person person = new Person();
person.Name = expected;

//act
string result = person.Name;

//assert
Assert.AreEqual(expected, result);
I wanted my assertions to read like:
//assert
result.ShouldBe(expected);
The usage.ShouldBe(easy to follow).

I do want to point out one special case for testing exceptions. The normal way of testing an exception and its message looks something like this:
[TestMethod]
[ExpectedException(typeof(InvalidOperationException))]
public void Save_InvalidPerson_ThrowsInvalidOperationException()
{
//arrange
PersonService service = new PersonService();

try
{
//act
service.Save(new Person());
}
catch (Exception exp)
{
Assert.AreEqual("Person is invalid", exp.Message);
throw;
}
}
This is a very simple example, but it is also rather verbose for what you get. Using a simple delegate reduces the amount of test code required to achieve the same result and increases the readability:
[TestMethod]
public void Save_InvalidPerson_ThrowsInvalidOperationException()
{
//arrange
PersonService service = new PersonService();

//act
Action action = () => service.Save(new Person());

//assert
action.ShouldThrow<InvalidOperationException>("Person is invalid.");
}
Just include the UnitTestExtensions class in your unit test project and take it for a spin. I hope that this will help you on the path to Zen Testing.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Envoc.Core.UnitTests.Extensions
{
public static class UnitTestExtensions
{
public static void ShouldBe<T>(this T actual, T expected)
{
ShouldBe(actual, expected, string.Empty);
}

public static void ShouldBe<T>(this T actual, T expected, string message)
{
Assert.AreEqual(expected, actual, message);
}

public static void ShouldNotBe<T>(this T actual, T expected)
{
ShouldNotBe(actual, expected, string.Empty);
}

public static void ShouldNotBe<T>(this T actual, T expected, string message)
{
Assert.AreNotEqual(expected, actual, message);
}

public static void ShouldContain(this string actual, string expected)
{
ShouldContain(actual, expected, string.Empty);
}

public static void ShouldContain(this string actual, string expected, string message)
{
Assert.IsTrue(actual.Contains(expected), message);
}

public static void ShouldNotContain(this string actual, string expected)
{
ShouldNotContain(actual, expected, string.Empty);
}

public static void ShouldNotContain(this string actual, string expected, string message)
{
Assert.IsFalse(actual.Contains(expected), message);
}

public static void ShouldBeSameAs<T>(this T actual, T expected) where T : class
{
ShouldBeSameAs(actual, expected, string.Empty);
}

public static void ShouldBeSameAs<T>(this T actual, T expected, string message) where T : class
{
Assert.AreSame(actual, expected, message);
}

public static void ShouldNotBeSameAs<T>(this T actual, T expected) where T : class
{
ShouldNotBeSameAs(actual, expected, string.Empty);
}

public static void ShouldNotBeSameAs<T>(this T actual, T expected, string message) where T : class
{
Assert.AreNotSame(actual, expected, message);
}

public static void ShouldBeEqual<T>(this T actual, T expected)
{
ShouldBeEqual(actual, expected, string.Empty);
}

public static void ShouldBeEqual<T>(this T actual, T expected, string message)
{
Assert.AreEqual(actual, expected, message);
}

public static void ShouldNotBeEqual<T>(this T actual, T expected)
{
ShouldNotBeEqual(actual, expected, string.Empty);
}

public static void ShouldNotBeEqual<T>(this T actual, T expected, string message)
{
Assert.AreNotEqual(actual, expected, message);
}

public static void ShouldBeLessThan<T>(this T actual, T expected) where T : IComparable
{
ShouldBeLessThan(actual, expected, string.Empty);
}

public static void ShouldBeLessThan<T>(this T actual, T expected, string message) where T : IComparable
{
Assert.IsTrue(actual.CompareTo(expected) < 0, message);
}

public static void ShouldBeGreaterThan<T>(this T actual, T expected) where T : IComparable
{
ShouldBeGreaterThan(actual, expected, string.Empty);
}

public static void ShouldBeGreaterThan<T>(this T actual, T expected, string message) where T : IComparable
{
Assert.IsTrue(actual.CompareTo(expected) > 0, message);
}

public static void ShouldBeNull(this object value)
{
Assert.IsNull(value);
}

public static void ShouldNotBeNull(this object value)
{
Assert.IsNotNull(value);
}

public static void ShouldThrow<T>(this Action action) where T : Exception
{
ShouldThrow<T>(action, string.Empty);
}

public static void ShouldThrow<T>(this Action action, string message) where T : Exception
{
string failMessage = string.Format("did not throw expected exception {0}.", typeof(T).Name);

try
{
action();
Assert.Fail(failMessage);
}
catch (Exception ex)
{
if (!string.IsNullOrWhiteSpace(message))
{
Assert.AreEqual(message, ex.Message);
}

Assert.AreEqual(typeof(T), ex.GetType(), failMessage);
}
}
}
}

No comments:

Post a Comment