Aug 6, 2010

Mocking Comparison – Part 12: The UnMockables

The frameworks we’ve been looking at in previous parts only work well when they can override the properties of the classes you wish to mock, or when you use interfaces. So what happens when you need to deal with a test that is time dependant, or if you want to verify data being written to the console or you have to deal with SharePoint or other similar products that are a dogs vomit of static classes, sealed types and leaky abstractions that make testing with them almost impossible using normal techniques?

The answer is to move away from regular mocking frameworks and use tools like TypeMock Isolator, Microsoft Moles or Telerik Just Mock (as a note, I don’t have a copy of JustMock so I’m going to leave it out of this series)

DateTime.Now

So let’s say you’ve got a class that references DateTime.Now and changes behaviour based on the time of day. How do you test behaviour correctly and more importantly,how do you do this reliably?  You can’t really mess with the system clock.  The trick is to intercept the call to DateTime.Now and provide your own method that gets called instead, and this is what TypeMock and Moles both allow you to do.

TypeMock

Here’s the basic code for mocking out DateTime.Now with TypeMock

readonly DateTime dateToUse = new DateTime(2010, 07, 17, 8, 0, 0);

[Fact]
public void Isolate_current_time()
{
Isolate.WhenCalled(() => DateTime.Now).WillReturn(dateToUse);
Assert.Equal(dateToUse, DateTime.Now);
}

Pretty simple stuff.

If you wanted to provide a method implementation rather the just returning a value you would replace the .WillReturn() call with .DoInstead()

So let’s see what this looks like in a test.  Once again, this is somewhat testing the mock which you shouldn’t do in the real world, but it does show you what the syntax is like, which is the goal after all.

Oh, as a reminder the relevant code from the class under test is as follows:

public void CleanMonkey()
{
// <snip!>
if (AssignedMonkey.IsAwake(DateTime.Now))
AssignedMonkey.Clean();
}

So in our test we are going to set the mock to only return true if the time passed to the IsAwake method matches our specific date time.  In other words, we should see that DateTime.Now is returning the value we specify, not the current time.

[Fact]
public void Isolate_monkey_should_be_awake()
{
Isolate.WhenCalled(() => DateTime.Now).WillReturn(dateToUse);

var monkey = Substitute.For<IMonkey>();
monkey.CurrentFleaCount().Returns(20);
monkey.IsAwake(Arg.Is(dateToUse)).Returns(true);

var keeper = new ZooKeeper();
keeper.AssignedMonkey = monkey;

keeper.CleanMonkey();

monkey.Received().Clean();
}

And as expected, this test works.  Now the keen eyed amongst you will see that I’m actually mixing TypeMock Isolator and NSubstitute in the one test.  I’m using Isolator to mock out the static DateTime.Now method and NSubstitute to create the mock monkey object.

Also, in terms of running, the tests there’s a few little changes to the tests and things take longer because Isolator is injecting itself in your code.  The other thing to note is that DateTime is part of the Base Class Library and TypeMock doesn’t support all of the methods in the BCL, just certain methods so if you want to mock out a method Isolator doesn’t support you’re on you own.

Mircosoft Moles

Moles is a framework that is usually obtained with the Pex download from Microsoft Research, but it’s also available in a standalone form.

Moles is still very much a product with some rough edges.  I’ve had moles randomly stop working with the only fix being an un/reinstall at times.

Now the good thing about Moles is that it can replace the method call for anything you want.  No limitations at all, however the way it does it is to inspect the assembly you want to mock and then it generates a separate buddy library that provides a way to mock out or stub the method calls you are interested in.  It’s fine when working with things like mscorlib or other assemblies that don’t really change, but if you’re applying it to your own libraries and they change method signatures regularly then you will need to regenerate the moles assemblies each time which is a pain.

The following code is using the Moles.Xunit extension to run the tests you can’t just run the tests via TestDriven.Net like you can with Isolator – you need to call the tests via the Moles TestRunner.  I use a batch file as follows to help with this:

cd c:\MyCode\bin\debug
"C:\Program Files (x86)\Microsoft Moles\bin\moles.runner.x86.exe" Monkeys.Moles.Tests.dll /runner:c:\path\xunit-1.5\xunit.console.x86.exe /x86
pause

You’ll also need to update assemblyinfo.cs to include attributes that tells Moles which methods you are using like so:

[assembly: MoledType(typeof(System.DateTime))]
[assembly: MoledType(typeof(System.Console))]

And once that’s in place finally you can write a test as follows:

[Fact]
[Moled]
public void Replace_current_time()
{
MDateTime.NowGet = () => dateToUse;
Assert.Equal(dateToUse, DateTime.Now);
}

[Fact]
[Moled]
public void Monkey_should_be_awake_for_cleaning_at_eight_am()
{
MDateTime.NowGet = () => dateToUse;
var monkey = Substitute.For<IMonkey>();
monkey.CurrentFleaCount().Returns(20);
monkey.IsAwake(Arg.Is(dateToUse)).Returns(true);

var keeper = new ZooKeeper();
keeper.AssignedMonkey = monkey;

keeper.CleanMonkey();

monkey.Received().Clean();
}

So here you may notice that we have an extra attribute on our test method to indicate that this test method uses Moles.  In addition we provide a lambda to MDateTime.NowGet instead of DateTime.Now.  This is the method in the buddy assembly that gets called when DateTime.Now is referenced.  Also, the method naming for Moled types gets a bit funny.  Methods are named based on the method you are mocking and then either a list of types based on the method being called or a Get/Set for properties.  It works, but it’s a little clunky.

Conclusion

So, my preference here from a syntax viewpoint is the Isolator syntax by far, though the fact that it can’t get to everything in the BCL (such as Console.WriteLine) and that it is a commercial product takes the shine off a little.  On the flip side I like the power of Moles but it’s very clunky to work with, breaks easily and is a pain to use when the target assembly changes a lot and the price and power doesn’t offset this problem.

That’s All Folks

And that, my friends, is that for this series.

I hope you’ve enjoyed it and have a good idea of what the various frameworks are capable of.  If you haven’t already guessed, my new favourite framework is NSubstitute.  If you haven’t already, go give it a try and see what you think and give feedback to the guys that wrote it on the NSubstitute mailing list.

Happy testing!

 

Other posts in this series:

5 comments:

  1. Hi Richard,

    I tried NSubstitute & I like it. I'm not expert in testing but I felt It makes tests more readable & simpler.

    I'm working on a project that consumes a 3rd-party library, TIBCO.EMS, that's a collection of concrete classes with internal constructors & without interfaces, briefly, full of UnMockables, as you call it. This is why I reached this article. I'd like to know if there are any plans to add handling for those unmockable cases in the future.

    Regards,
    Waheed Sayed

    ReplyDelete
  2. Hi,

    I started using MOQ, can u plz tell how can I moq two objects of same definition, I created two moqs but its taking the last moq for the both objects

    ReplyDelete
  3. Is ms Fakes 2012 better than Moles 2010?

    ReplyDelete
  4. The danger with any Mock framework that allows you to mock concrete class such as Fakes, Moles, TypeMock is that it can be abused by the lazy or inexperienced developer. Used sparingly and where appropriate it can be used to great advantage but it should not be used to circumnavigate good SOLID design.

    ReplyDelete