The basics of Microsoft Fakes

  .Net Development Unit Testing Tutorial

Mocking is an integral part of Unit Testing. There are several frameworks that exist for the .Net Framework to achieve such functionality. At work we use Typemock and it has a fantastic range of features. However, it has some major problems at the moment when used with Visual Studio 2012. Also if a programmer not quite familiar with the ins and outs of the API gets their hands on on a unit test, it can cause havoc on the build server. Just a quick hint here, if you're using Typemock and unit tests only pass on a development machine, you're probably missing this:

[TestCleanup] 
public void CleanUp() 

    Isolate.Cleanup(); 
}

Anyway, back to the subject at hand, I couldn't afford a full license for Typemock for my personal use at home, so I went looking for alternatives. I always like to use Microsoft's built in tools if I can, so when I stumbled upon Fakes, I thought I'd give it a try. I've got to say from the start, Typemock is better. Microsoft just made PrivateAccessors obsolete because it required adding groups of classes just for unit testing. Instead they created the PrivateObject and PrivateType objects to use reflection to access private members in unit tests. It then seems odd that considering this, the Fakes framework does exactly what PrivateAccessors used to do. But hey, who am I to judge. My point is that Typemock uses code such as:

 IExample example = Isolate.Fake.Instance<IExample>(); 

to create mock objects. This works great, so long as you remember to cleanup. Hence I much prefer it. Now lets dive into Fakes.

Lets write a quick class as an example.

namespace ShimAndFakeExample
{
    using ShimAndFakeExample.ServiceReference;
    using System;
    public class ExampleClass : ExampleBaseClass
    {
        protected override decimal Add(decimal number, decimal addThisNumber)
        {
            if (DateTime.Now.Hour <= 12)
            {
                return number;
            }
            return number + addThisNumber;
        }
        public string CallWcfService()
        {
            var client = new WcfTestClient();
            const string message = "Whatcha talkin' about WCF?";
            return client.GetMessage(message);
        }
    }
}

We've got a class that inherits from an abstract class with a protected method; Add, which for the purposes of this example, add's two numbers together as long as the time of day is before noon. There is also a method that calls a WCF service. Now let's unit test!

First we'll test the functionality of our Add method. As it is a protected method we'll need a PrivateObject and we'll make it accessible accross all tests.

private PrivateObject _accessor; 
[TestInitialize] 
public void SetupTest() 
{
     _accessor = new PrivateObject(typeof(ExampleClass)); 
}
Add Fakes Assembly

Add Fakes Assembly

Our method relies on the time of day, and we can't have our unit tests failing because of this. What we need to do is intercept the call to DateTime.Now and inject a value that we want to test. To start with, we need to add a Fakes Assembly. We do this by right clicking on the reference and choose "Add Fakes Assembly". We can then add the code to inject the value into the test.

[TestMethod] 
public void TestAddBeforeNoon() 

    using (ShimsContext.Create()) 
    { 
        const decimal expected = 15M; 
        const decimal addition = 10M; 
        ShimDateTime.NowGet = () => new DateTime(2000, 1, 1, 11, 0, 0); 
        var actual = _accessor.Invoke( 
            "Add",
            BindingFlags.FlattenHierarchy, 
            new Type[] { typeof(decimal), typeof(decimal) }, 
            new object[]{ expected, addition }); Assert.AreEqual(expected, actual); 
     }
}

As we are faking the functionality of a compiled assembly we have to Shim it. Fakes dictates that we must do all testing that uses a Shim inside of a ShimContext. We can then use a lambda expression to add a delegate that returns an instance of a DateTime that has the time we want to test, in this case 11:00. We can then invoke the method for our test. Now we'll look at testing our WCF Service call.

The WCF Service will have its own collection of unit tests and it really shouldn't be our place as a client of the service to test its functionality. We also shouldn't be calling it in a unit test. Let's Fake it.

[TestMethod] 
public void TestCallWcfService()
 { 
    using (ShimsContext.Create()) 
    { 
        ShimWcfTestClient.Constructor = client => new ShimWcfTestClient(); 
        ShimWcfTestClient.AllInstances.GetMessageString = (client, s) => s; 
        Assert.IsNotNull(new ExampleClass().CallWcfService()); 
    
}

Our code instantiates a new instance of the WCF Client which will in turn look for entries in our App.config. We probably don't want to have the end point information in the configuration for our unit test assembly so what we'll do is intercept the call to the constructor so that the base class isn't called and looks for the end point information. We then fake the return value of the operation of all the instances of the service client our unit test creates.

I'll finish off the post with a quick thank you for reading, the Code Coverage Results, the Test Results and the complete code.



namespace ShimAndFakeExampleTests 

    using Microsoft.QualityTools.Testing.Fakes; 
    using Microsoft.VisualStudio.TestTools.UnitTesting; 
    using ShimAndFakeExample.ServiceReference.Fakes; 
    using System; using System.Fakes; 
    using System.Reflection; 
    [TestClass] 
    public class ExampleClassTests 
    
        private PrivateObject _accessor; 
        [TestInitialize] 
        public void SetupTest() 
        
            _accessor = new PrivateObject(typeof(ExampleClass)); 
        }
 
        [TestMethod] 
        public void TestAddBeforeNoon() 
        
            using (ShimsContext.Create()) 
            
                const decimal expected = 15M; 
                const decimal addition = 10M;
                ShimDateTime.NowGet = () => new DateTime(2000, 1, 1, 11, 0, 0); 
                var actual = _accessor.Invoke( "Add", BindingFlags.FlattenHierarchy, new Type[] { typeof(decimal), typeof(decimal) }, new object[]{ expected, addition }); 
                Assert.AreEqual(expected, actual); 
            
        
        [TestMethod] 
        public void TestAddAfterNoon() 
        
            using (ShimsContext.Create()) 
            
                const decimal number = 15M; 
                const decimal addition = 10M; 
                ShimDateTime.NowGet = () => new DateTime(2000, 1, 1, 13, 0, 0); 
                const decimal expected = number + addition; 
                var actual = _accessor.Invoke( "Add", BindingFlags.FlattenHierarchy, new Type[] { typeof(decimal), typeof(decimal) }, new object[] { number, addition }); Assert.AreEqual(expected, actual); 
            
        }
        [TestMethod] 
        public void TestCallWcfService() 
        
            using (ShimsContext.Create()) 
            
                ShimWcfTestClient.Constructor = client => new ShimWcfTestClient(); 
                ShimWcfTestClient.AllInstances.GetMessageString = (client, s) => s; 
                Assert.IsNotNull(new ExampleClass().CallWcfService());
            
        
    
}
comments powered by Disqus