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.

7 comments:

  1. Thanks Patrik.

    Well, I only started with FIE on Friday 1st (and it's my first time with any faking/mocking framework), so I'm not going to be upset!

    Re. documentation for FIE, would you like some help with that? E.g. I could presumably run your DLL and XML file thru Sandcastle and get a (reasonably) nice .CHM file out. Plus an entry on Wikibooks might be useful.

    I'm sure people who are used to Rhino or Moq can pick up FIE without documentation, but I'm thinking of experienced .NET developers who are new to faking/mocking (like me).

    Do you have plans to provide documentation, or would you like assistance with that?

    ReplyDelete
  2. Sorry for the kind of late answer but I'm on vacation.

    I would of course LOVE for any help on FakeItEasy, what ever it may be. Documentation is something that's clearly lacking. It would be cool to have SandCastle set up in the build script so that it's automatically generated when building.

    Also, the wiki idea is great. Ping me at patrik@hagne.se

    ReplyDelete
  3. Hey Patrik,

    I was going to point Andrew to http://ondevelopment.blogspot.com/2010/05/fakeiteasy-login-service-example-series.html but it turns out that of course the code from the Mercurial doesn't work anymore following your changes.

    Any chance of updating the code to work with version 1.7? I'm pretty sure that other beginners to FakeItEasy, like myself, will find it really useful in their learning curve.

    ReplyDelete
  4. That's definitely something I'll try to look at as soon as possible.

    ReplyDelete
  5. Thanks for that! Looking forward to it! Btw great work with FIE! Now that I understand stubs and mocks, I realise the value you've added although it could potentially encourage bad practice. But I digress...

    ReplyDelete
  6. Hm, ...I've used the pattern: A.That.Contains("foo").And.Not.Contains("bar") quite often. How do you suggest this be solved in >=1.7 then?

    ReplyDelete
  7. Tjena!
    Sorry for being slightly off-topic but I can't find any other way to contact you. Just wanted to say that right now I am replacing some rhinomocks with easy fakes and things are getting pretty nice and clean. <3

    ReplyDelete