Tuesday, August 7, 2012

FakeItEasy with Action/Action Arguments

Preface

I discovered I couldn't use my go-to mock framework, NSubstitute, when unit testing Silverlight applications. A quick Google scan lead me to FakeItEasy, so I thought I'd give it a try. It's pretty intuitive, and has a decent, fluent interface. Here are some examples from the FakeItEasy Wiki.

A.CallTo(() => foo.Bar()).MustHaveHappened();
A.CallTo(() => foo.Bar()).MustNotHaveHappened();
A.CallTo(() => foo.Bar()).MustHaveHappened(Repeated.AtLeast.Once);
A.CallTo(() => foo.Bar()).MustHaveHappened(Repeated.Never);
A.CallTo(() => foo.Bar()).MustHaveHappened(Repeated.NoMoreThan.Times(4));
A.CallTo(() => foo.Bar()).MustHaveHappened(Repeated.Exactly.Twice);

My Problem

One of the services in the application I've been working on passes a lot of callbacks as parameters. They take the form of Action, Action<T>, Action<T1, T2>, etc. Since I had to be able to test these things, I had to figure a way to invoke the callback in the test. Here's an example of what I was dealing with:

public interface IFoo
{
    void Bar(int number, Action<bool> callback);
}

The Solution

It turns out the solution was pretty simple. FakeItEasy has an extension method (.Invokes()) which can invoke other delegates. This made it possible to extract and invoke the Action callback.

Action Type

When testing something with a simple Action parameter:

public interface IFoo
{
    void Bar(Action callback);
}

FakeItEasy can be used to execute the action. This is done by using the Invokes() extension method. We grab the argument we want, cast it to an Action, then call the action.

var foo = A.Fake<IFoo>();
A.CallTo(() => foo.Bar(A<Action>.Ignored))
               .Invokes(call =>
               {
                   var action = ((Action)call.Arguments.First());
                   action();
               }
         );

Action<T> Type

It's equally easy to handle actions that have one or more parameters. Below is an interface with a method that has two arguments. The Action<bool> callback is the one we want to grab.

public interface IFoo
{
    void Bar(string name, Action<bool> callback);
}

The solution is very close to the previous one. The Invokes() extension method is again used to access the call's arguments. Since the Action<bool> callback is the second argument of the method, the Linq First() expression is dropped in favor of the array. That argument is then cast and called.

var foo = A.Fake<IFoo>();

A.CallTo(() => foo.Bar(A<Action<bool>>.Ignored))
         .Invokes(call =>
         {
             var action = ((Action<bool>)call.Arguments[2]);
             action(true);
         });

Summary

I'm using FakeItEasy more often lately, because it can be used in more project types. Its interface is fluent and intuitive. I hope the example I gave will help others who are starting to check it out.