Practical .NET

Introducing TDD With The Microsoft Fakes Framework

One of the major reasons that developers don't like TDD is because, inevitably, it leads to mocking—which can be time consuming. Microsoft has created the Fakes Framework just to simplify the whole process.

To a certain extent, this is another instance if the Impractical .NET column: I'm going to talk about a feature of Visual Studio 2012 (which you're probably not using yet) and, furthermore, a feature only available in the Ultimate edition (and you'll probably be using the Professional edition). But it's something of which you should be aware; it might even prompt you take a look at the newer stuff.

It's no secret that I'm a big fan of Test-Driven Development (TDD). The big issue with TDD for most developers is, I think, the database. If your tests are driven by your data, and your data keeps changing, how do you to ensure that you get the "right" answer in your automated tests? However, that's just one example of a larger problem: If the code you're testing is dependent on something else, and that something else keep changing, how do you ensure that your test results stay constant?

Fortunately, in an object-oriented world it all comes down to managing objects. Sure, there's some part of your application that directly interacts with your data; but in the end, that data gets converted into some object (if only rows in a DataSet).

That's where mocking comes in: you swap in a mock object whose outputs you have complete control over, to replace the real thing that might be changing. In fact, I often create multiple versions of any mock object, with each version returning a result that supports a specific set of tests (Can my code handle an ordinary Customer object? How about one with Null fields? A Customer with a bad credit rating?)

The issue here, then, is to make creating mock objects as simple and easy as possible. If the cost of creating/managing mocks is hard, time-consuming, or expensive then developers won't do TDD—it would just take too much time. This is where tools like TypeMock and, now, Microsoft Fakes Framework come in. With Fakes you just add a Using block and the mock member (method or property) you want to control.

The Fakes Framework
The Fakes Framework actually provides two ways to create mock objects: stubs and shims. Stubs require that you write your code a specific way (basically, declare all your object variables using interfaces rather than classes), and it won't let you mock a variety of typical class features (basically, any member that can't be overridden like static/shared methods).

Stubs do, however, give you better performance when executing tests. Generally speaking, though, I don't usually run a lot of tests at one time (I'm usually just running the test for the method I'm working on right now), so I'm not concerned about performance.

Shims, on the other hand, work under all circumstances and with any member of a class (though they're supposed to give you poorer performance). As a result I assume that I'm going to be working with shims when using Fakes—less thought required.

For this example of the Fakes Framework in action, I created a Class Library project with some math functions in it (this keeps the code simple, so I can focus on showing off shims). My MathFuncs class has exactly one method in it, that adds two numbers. However, inside that method, I call a method name -- ConvertAnythingToInteger -- on a class called Conversion:

Public Function AddTwoIntegers(a As Object, b As Object) As Integer
Dim cati As New Conversion

Return cati.ConvertAnythingToInteger(a) + 
       cati.ConvertAnythingToInteger(b)

End Function

I then added a Test project to the solution and gave it a reference that points to the Class Library project I want to test. With the Test project in place, I can set up to use the Fakes Framework with the project under test: In Solution Explorer, select Show All Files, expand the References node to show all the references in the Test project to find the DLL for project being tested, right-mouse click on the DLL and select Add Fakes Assembly from the pop-up menu.

Writing Fake Tests
For my first test, I would like to test just my AddTwoIntegers method—I don't want to also test the ConvertAnythingToInteger method (I'm assuming that the Conversion class is someone else's problem or, even it's my problem, a problem for another day). To isolate AddTwoIntegers from ConvertAnythingToInteger, I want to insert a shim that will intercept all calls to the ConvertAnythingToInteger method and return some fixed value. The first step is, in my test code, to set up a context that allows me to insert shims:

Using sc = Microsoft.QualityTools.Testing.Fakes.ShimsContext.Create()
            
End Using

Then, within that Using block, I define my shim. IntelliSense reveals that, in the namespace for my Class Library project (MathStuff—the one to which I added the Fakes assembly), I have a new namespace called Fakes.

In addition, that Fakes namespace has a new class for every class in the project under test -- the only difference being that these new classes have names beginning with "Shim". That means, in this case, that in the Mathstuff.Fakes namespace I now have a class called ShimConversion.

IntelliSense also tells me that, under the class's AllInstances member, I have members corresponding to the members on my class -- though, again, with a slight change in their names. Method names, for instance, are extended with the types of the parameters they accept. In this case, that means I have a member called ConvertAnythingToIntegerObject because ConvertAnythingToIntegerObject accepts a single parameter of type Object.

To create my shim, I set that member to some anonymous function which will be called, instead of the real ConvertAnythingToInteger method. This example causes the method to return the number 4 every time:

MathStuff.Fakes.ShimConversion.AllInstances.ConvertAnythingToIntegerObject =
  Function() As Integer
    Return 4
  End Function

The actual test code is independent of the shim, as long as you put that code in the Using block. So now I write my test:

Dim mh As New SampleFakes.MathFuncs
res = mh.AddTwoIntegers(5, 7)
Assert.AreEqual(8, res, "Unable to add two integers")

This is, obviously, just the tip of the iceberg. In my next column I'll be going into a little more depth.

About the Author

Peter Vogel is a system architect and principal in PH&V Information Services. PH&V provides full-stack consulting from UX design through object modeling to database design. Peter tweets about his VSM columns with the hashtag #vogelarticles. His blog posts on user experience design can be found at http://blog.learningtree.com/tag/ui/.

comments powered by Disqus

Featured

Subscribe on YouTube