This is part 2 of Using Microsoft Fakes. If you missed the first one, check it out.

In the first part of this series I talked about using Stubs to unit test your code and they work great but sometimes you don’t have control over everything, you can’t exactly inject the System dll into your code. So how do you unit test logic that has a dependency on something you have don’t control over? Enter Shims.

Shims allow you to intercept a call at run time. This differs slightly from the stubs where we are passing in the stubbed out code. Very small difference, and the technique is very similar.

Using Shims

I am going to use the same example as in my previous post where I trying to test out some business logic for saving a person, but in this case I am going to be using as GUID as an identifier instead of an integer getting passed back from the data access layer.

        public Guid Save(Person p) {
            p.Name = "Changed Name";
            p.UniqueId = Guid.NewGuid();
 
            _personData.SavePerson(p);
            return p.UniqueId;
        }

So I don’t have control over the get new Guid. There is no way I can inject this functionality and no way that I can test to make sure a specific Guid gets assigned. I realize this is a bit contrived since you would never need to know a specific Guid gets generated, but hey this is just an example of usage, no one ever said it had to make sense!

Ok, so now that I have that disclaimer out of the way, let’s move onto the unit test code. Just as before I am going to inject the stubbed out version of IPersonData, but now I am now to put a shim in front of the new Guid.

using System;
using System.Fakes;
using Data;
using Data.Fakes;
using DataContracts;
using FakesTemp; //The name of my business logic class
using Microsoft.QualityTools.Testing.Fakes;
using Microsoft.VisualStudio.TestTools.UnitTesting;
 
namespace testProj {
    [TestClass]
    public class PersonTests {
        [TestMethod]
        public void PersonTests_HappyPath()
        {
            IPersonData personData = new StubIPersonData
            {
                SavePersonPerson = person => { return person.Age; }
            };
 
            PersonManager target = new PersonManager(personData);
 
            var p = new Person
                {
                    Age = 28,
                    Name = "Bruce Campbell"
                };
 
 
            Guid expected = new Guid("187695AC-1EE6-4BB4-BB71-366DBE9C8D0D");
            using (ShimsContext.Create())
            {
                ShimGuid.NewGuid = () => new Guid("187695AC-1EE6-4BB4-BB71-366DBE9C8D0D");
                Guid actual = target.Save(p);
                
                Assert.AreEqual(expected, actual);
            }
        }
    }
}

A few things to note. In order to generate the shim for the Guid.New I had to create a fakes assembly for the System dll. To do this you simply right click on the dll in your references folder in solution explorer and click on the add fakes assembly option. After that just add the using statement for the new System.Fakes assembly.

To get started using Shims, you must first create a ShimContext. This defines the scope for the shim, any shim that you create in this block will be used anywhere within the call. So if I had multiple calls to Guid.NewGuid() throughout the execution of my test they would all get the same Guid. So just keep in mind that using shims for built in code can cause some weird results if you aren’t careful.

In order to actually create the shim you simply preface whatever object you are shimming with the word shim. Take a second for that to sink in. That’s pretty much it. From there you define what specific method you are overriding, and then tell it what you want the value to be. Very very simple.

Summary

  • Using shims allows you to overwrite inbuilt methods at run time of your unit test
  • You should limit the scope of your shim to as narrow as possible to reduce the risk of unintended shimming
  • Combining both Stubs and Shims allows you to really isolate your code for testing
  • Unit testing is AWESOME!

This is also posted on my company blog