Sunday, April 10, 2011

An alternative to NotImplementedException

It happens from time to time that I forget to implement some code where I’m just throwing a NotImplementedException. Being forgetful I have a habit of coming up with stuff that will help me not to forget. I think my “MustBeImplementedException” is quite useful, it will break the build if the build is not DEBUG.

#if DEBUG

namespace FakeItEasy
{
    using System;
    using System.Diagnostics.CodeAnalysis;

    /// <summary>
    /// An exception that can be thrown before a member has been
    /// implemented, will cause the build to fail when not built in 
    /// debug mode.
    /// </summary>
    [Serializable]
    [SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors", Justification = "Never used in production.")]
    public class MustBeImplementedException
        : Exception
    {
    }
}

#endif

Saturday, April 2, 2011

An old pain point removed from FakeItEasy

It’s always bittersweet to say that you have introduced a breaking change in an API that people use. Bitter because it will break peoples code. But sweet because IT’S AN AWESOME NEW FEATURE!

I was never really fond of the fact that argument constraints relies on implicit casting in FakeItEasy, this is why you had to write A<IFormattable>.Ignored.Argument but you could write the shorter A<string>.Ignored for non interface types. This had to be done in order for the chaining of argument constraints to work: A<string>.That.Contains(“foo”).And.Not.Contains(“bar”) for example. I thought I was pretty smart when I implemented this, it turns out though that ONCE in SEVERAL THOUSAND tests have I used this feature myself. Smart is never simple, smart is quite often stupid.

Now, I don’t wanna be stupid, so what can I do? I can remove the stupid feature. Said and done, in versions >= 1.7.* you can no longer chain argument constraints (but you weren’t doing that anyway).

Things that will break:

First of all, everything breaks at compile time so you won’t be able to build without fixing the problems. This is one of the benefits of a static type system, the complier will tell you what’s wrong!

.Argument

Any argument constraint will now return an instance of the constrained type (T) and not an ArgumentConstraint<T> as it did in previous versions. This means that there is not property .Argument so you will have to remove any calls to that property from your tests. This might be quite many calls depending on how many tests you have and what you have been testing.

ArgumentConstraint<T>

As I said this class is no more (RIP). The lowest level api for creating constraints is now the “Matches(Func<T, bool> predicate, Action<IOutputWriter> descriptionWriter)” on the IArgumentConstraintManager<T> type. You access an argument constraint manager through the A<T>.That-property. This means that you can much more easily now provide extensions to the That-property, all you have to do is to create an extension method on IArgumentConstraintManager<T> and call the Matches-method inside your extension method. Here’s an example on how the IsNull() argument constraint is implemeneted.

/// <summary>
/// Constrains an argument so that it must be null (Nothing in VB).
/// </summary>
/// <typeparam name="T">The type of the argument.</typeparam>
/// <param name="manager">The constraint manager to match the constraint.</param>
/// <returns>A dummy argument value.</returns>
public static T IsNull<T>(this IArgumentConstraintManager<T> manager) where T : class
{
    return manager.Matches(x => x == null, x => x.Write("NULL"));
}

If you have extended the That-property before all your extensions will have to be rewritten to use the new API, it’s quite a simple task though and you’ll be glad that it will be much easier to create new extensions.