Sunday, September 26, 2010

Extending exception messages in FakeItEasy

One of the main features of FakeItEasy is the informative exception messages, let’s say we have the following types:

public interface IPersonRepository
{
    void Save(Person personToSave);
}

public class Person
{
    public string FirstName { get; set; }

    public string LastName { get; set; }

    public DateTime DateOfBirth { get; set; }
}

Now, let’s create a test that shows us what the exception messages in fake it easy looks like when asserting:

public void Extending_exception_messages()
{
    var repository = A.Fake<IPersonRepository>();

    repository.Save(new Person { FirstName = "Patrik", LastName = "Hägne", DateOfBirth = new DateTime(1977, 4, 5) });

    A.CallTo(() => repository.Save(A<Person>.Ignored)).MustNotHaveHappened();
}

This will yield an exception message that looks like this:

  Assertion failed for the following call:
    'FakeItEasy.Examples.IPersonRepository.Save()'
  Expected to find it exactly never but found it #1 times among the calls:
    1.  'FakeItEasy.Examples.IPersonRepository.Save(
            personToSave: FakeItEasy.Examples.Person)'

While this is a good exception message it doesn't say a whole lot about the person object that was used in the call to save. You could ofcourse in this particular instance override the ToString-method of person to fix this, but a lot of times you don’t have control over the types you use (for example the framework types). Now, fortunately, in the latest release of FakeItEasy, there is a dead simple way for you to provide your own formatter for printed argument values.

All you have to do is in your testproject define a class that inherits the FakeItEasy.ArgumentValueFormatter<T>-class where T is the type of class you wish to provide custom formatting for.

Here’s an example of such a formatter for the Person-type:

public class PersonArgumentFormatter
    : ArgumentValueFormatter<Person>
{
    protected override string GetStringValue(Person argumentValue)
    {
        return string.Format("Person named {0} {1}, date of birth {2:yyyy-MM-dd} ({3} days old).",
            argumentValue.FirstName,
            argumentValue.LastName,
            argumentValue.DateOfBirth,
            DateTime.Now.Subtract(argumentValue.DateOfBirth).TotalDays);
    }
}

Now, let’s run the same test again and see what the exception message looks like: (I’ve split the message over several lines in this post so that the message will not be truncated by the blog).

  Assertion failed for the following call:
    'FakeItEasy.Examples.IPersonRepository.Save()'
  Expected to find it exactly never but found it #1 times among the calls:
    1.  'FakeItEasy.Examples.IPersonRepository.Save(
            personToSave: Person named Patrik Hägne,
                          date of birth 1977-04-05 (12227,874689919 days old).)'