Creating Custom NUnit Assertions
 
Published: 03 Aug 2007
Abstract
This article discusses how to create assertions in the NUnit framework. NUnit has added some assertion classes to its framework; this article will show you how to extend it to meet your testing needs.
by Brian Mains
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 34020/ 91

Introduction

NUnit is an excellent utility in supporting application development. Based on JUnit, NUnit tests .NET applications by making assertions about the code, allowing you to compare properties within your objects for certain values. For the unit test to succeed, all of the assertions must evaluate to true, or the test fails. There are complexities with unit tests; not every object has public properties, which makes it harder to test.  It is possible to do through the Reflection API objects which inspect the metadata and get the value of the property.

Some complexities with application code exist, such as classes marked with the friend/internal class modifier.  If the unit test exists outside of that project, it is not possible to test these types, except through the classes that use them, with one exception: adding the InternalsVisibleTo assembly attribute for the project that has the internal types.  InternalsVisibleTo references an assembly that permits its internal types to be visible to that class by designating the assembly information for that project. Though MSDN documentation states that the projects it references should be strongly-typed, I have read blog entries that use local assemblies and have done it in one of my projects as well.

I wrote an article about using a product called TypeMock, which is a powerful mocking library capable of performing complex unit tests.  All of these are features that are very helpful when creating unit tests.  And lastly, the newest version of NUnit contains additional assert objects for comparing collections, files, and strings, all of which are very useful and handy to have.

That got me curious, as I like the ability to have additional assertions than the standard set.  Additional assertion objects have the ability to add a powerful set of assertion features that can make some of the unit testing easier.  For instance, it would be nice to have assertions that reflected against an object, or assertions that poll a data table or row.  This article is going to go further into that, examining some of the essential objects, such as the various types of constraint objects that perform the matching of the objects.

New Assertions

The newest version of NUnit (at the time of this writing, 2.4.1) features some brand new assertion classes. There is the ability to compare collections with CollectionAssert, determining whether each item in a collection is unique, not null, are instances of a specific type, etc. The requirement for this class is that the collection implements ICollection (some of the collections in .NET 2.0 implement the generic version).

In addition to that, there is the StringAssert type that determines if one string contains another string, whether one string starts with a value, whether one string ends with a value, or if they are equal when case typing is ignored.  FileAssert determines whether two files do or do not match, using the file path or stream objects to do the comparison.

In addition, Assert.That uses contraints to perform the comparison of the values. To make it easier, NUnit contains SyntaxHelpers namespace with an Is, Has, List, or Text helpers that contain a series of properties that return constraint-based objects to perform constraint matching for you.  For instance, the following are several of the constraints that are possible.  If you use the reflector tool to look at the source code, you will see that this new method of performing comparisons, using constraints, is used a lot in the NUnit assertions.

Listing 1

Assert.That("My Text Value", Text.StartsWith("My"));
 Assert.That(objValue1, Is.EqualTo(objValue2));
Assert.That(objValue1, Is.SameAs(objValue2));

This article will show you how to create your own assertions with custom constraints. Creating custom assertions can be helpful to perform more advanced comparisons of the data in your project.

Custom Assertion Classes

Oftentimes, I have a need to parse the data in a data row or table, to ensure that certain known data exists within it. That way I can be sure that the returned result set is correct.  Because of this, a custom assertion can be a parent static class that reuses the regular assertions of the NUnit framework, while providing additional capabilities, such as strong typing.

This assert class is a static class that exposes a series of methods. Most of the NUnit classes have three sets of methods for a single type of assert.

Listing 2

public static void ColumnIsNotNull(DataTable table, string columnName) { }
public static void ColumnIsNotNull(DataTable table, string columnName, 
  string message) { }
public static void ColumnIsNotNull(DataTable table, string columnName, 
  string message, params object[] parameters) { }

When determining if a column is not null, the first method defines the base assertion criteria and the last two allow a personalized message. The assertion classes use a constraint class that implements the IConstraint interface or the Constraint base class. Any constraints, including custom constraints, can be used as such and the constraint works well with the Assert.That method.

Listing 3

Assert.That(table, new DataTableNullConstraint(columnName, notNull),
  message, parameters);

DataTableNullConstraint is a custom constraint used to determine whether the expected criteria meet the actual. In some cases the expected and actual values are blatantly clear, such as with Assert.AreEqual. It contains two values of a specified type that can be compared. In some cases, these are not blatant, such as the Assert.IsNotNull method, as the expected and actual requirements depend on the null status of an object.

With this custom contraint, any expected criteria are passed through the constructor or factory method of the constraint. The actual is passed through one of the abstract methods, called Matches. The Matches method returns a boolean value for whether the expected value matches the actual (which is passed into the method).

In addition to Matches, the WriteDescriptionTo method is also abstract and used to write the error information to the screen. The MessageWriter object has multiple write methods, such as WriteExpected and WriteActual to write the expected/actual values. The WriteMessageLine method writes a descriptive message about the problem, the WriteValue method writes a value to the string, and there are other methods available as well.

Data Table Asserts

The following is a constraint that checks for nulls in a DataTable, whether a column is not null or must only be null. The constraint has two constructors, one for the column name or index, and one for whether to check for nulls or not nulls, as shown below.

Listing 4

public class DataTableNullConstraint: NUnit.Framework.Constraints.Constraint
{
  private int _columnIndex =  - 1;
  private string _columnName = null;
  private bool _notNull = true;
 
  public DataTableNullConstraint(string columnName, bool notNull)
  {
    _columnName = columnName;
    _notNull = notNull;
  }
 
  public DataTableNullConstraint(int columnIndex, bool notNull)
  {
    _columnIndex = columnIndex;
    _notNull = notNull;
  }
}

The constraint uses the WriteDescriptionTo method to write a message line about the actual problem (whether the table has a null or not null value), using the MessageWriter object to write the message.

Listing 5

public override void WriteDescriptionTo(MessageWriter writer)
{
  writer.WriteMessageLine("The data table contains a {0} value", 
  _notNull ? "null" : "not null");
}

The matching of the object, in the Matches method, parses the table (passed in through the parameter) and determines whether fields have or do not have null values for a column.

Listing 6

public override bool Matches(object actual)
{
  base.actual = actual;
  if (actual == null || !(actual is DataTable))
    throw new ArgumentException("Actual value is not a DataTable""actual");
 
  DataTable table = actual as DataTable;
 
  foreach (DataRow row in table.Rows)
  {
    bool isNull = !string.IsNullOrEmpty(_columnName) ? row.IsNull(_columnName):
      row.IsNull(_columnIndex);
    if ((_notNull && isNull) || (!_notNull && !isNull))
      return false;
  }
 
  return true;
}

The first statement of this method is recommended by the NUnit documentation, assigning the actual value to the base method. It also validates the actual value, and if not a data table, an exception is thrown.

For each row in the table, if the column or index value is null or not null (depending on what is being searched for), a false is returned indicating a problem. When a false is returned to the caller, through Assert.That(), a false value means that an AssertionException exception is raised.  How this constructor is exposed is through a static assert class. Below is the method that looks for nulls:

Listing 7

public static void ColumnIsNotNull(DataTable table, string columnName, 
 string message, params object[] parameters)
{
  ColumnIs(table, columnName, true, message, parameters);
}
public static void ColumnIsNotNull(DataTable table, int columnIndex, 
 string message, params object[] parameters)
{
  ColumnIs(table, columnIndex, true, message, parameters);
}

I left out the other four overloads for brevity, as they pass the information directly along. The private ColumnIs global methods use the Assert.That method, creating a constraint and passing along the message parameters.

Listing 8

private static void ColumnIs(DataTable table, string columnName, bool notNull, 
 string message, params object[] parameters)
{
  Assert.That(table, new DataTableNullConstraint(columnName, notNull),
  message, parameters);
}
 
private static void ColumnIs(DataTable table, int columnIndex, bool notNull, 
 string message, params object[] parameters)
{
  Assert.That(table, new DataTableNullConstraint(columnIndex, notNull), 
  message, parameters);
}

If Matches returns false, an AssertionException is thrown and a call to the WriteDescriptionTo method renders any error details. That was a simplistic illustration.  Let us now take a look at one that uses reflection. I also built a ReflectionAssert which has the ability to compare two object's properties to ensure they are the same.

Listing 9

public override bool Matches(object actual)
{
  base.actual = actual;
  if (actual == null)
    throw new ArgumentNullException("actual");
 
  if (!string.IsNullOrEmpty(_propertyName))
  {
    PropertyInfo prop = _expectedObject.GetType().GetProperty(_propertyName);
    if (prop == null)
      throw new ArgumentNullException("prop");
    return prop.GetValue(_expectedObject, null).Equals(prop.GetValue(actual,
      null));
  }
  else
  {
    PropertyInfo[]props = _expectedObject.GetType().GetProperties();
    if (props == null || props.Length == 0)
      throw new ArgumentNullException("props");
 
    foreach (PropertyInfo prop in props)
    {
      if (!prop.GetValue(_expectedObject, null).Equals(prop.GetValue(actual,
        null)))
        return false;
    }
 
    return true;
  }
}

This example can be used to parse a single property, or parse multiple property values, depending on what expected criteria is provided.  If parsing all properties and only one of the values does not match, then a false value is returned; otherwise, true is returned.

Conclusion

Custom asserts are very helpful in testing .NET applications, especially in testing code that is repetitive, where an assertion may provide an additional benefit. You can find these asserts and more at http://www.codeplex.com/nucleo.



User Comments

Title: NUnit Test Generator   
Name: Thinzar
Date: 2012-07-02 1:55:11 AM
Comment:
I want to write NUnit Test generator tool. Pls tell me what features would be useful to add.
Title: NUnit Test Generator   
Name: Greg Finzer
Date: 2007-09-28 12:21:06 PM
Comment:
Brian, Thanks for the great article. I recently created a tool to create NUnit Tests. What other features would be useful to add?

http://www.kellermansoftware.com/p-30-nunit-test-generator.aspx

Product Spotlight
Product Spotlight 





Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2021 ASPAlliance.com  |  Page Processed at 2021-12-01 8:14:15 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search