Isolation of the dependencies in code by mocking or faking objects is one of the common norms when developing unit tests or creating a Test-driven development. People write mocking objects that can be used to redirect calls to some dependent objects in order to independently test modules. There are also a number of frameworks that generate mocking objects automatically based on the existing dependencies without having to use any handwritten objects. The Visual Studio 2013 Ultimate edition comes with a new Microsoft Fakes framework that takes mocking to a new level.
If you have used Microsoft Moles before, you will find that the Fakes framework has a very similar workflow. Moles was originally designed for Microsoft Pex, which is another great unit testing tool by Microsoft Research. Moles became popular because it could virtually create mocks on anything and was so simple that there was hardly any overhead on using it. Moles provided a type safe detour framework with very clear semantics. For example, any .NET method in Moles is represented by a delegate. Microsoft replaces Moles in Visual Studio 2013 with the Microsoft Fakes framework, with the working principles defined almost the same as it.
Microsoft Fakes uses stubs and shims to replace other parts of the dependencies while testing a unit. These are small pieces of code that are used in tests. Thus, replacing unnecessary dependencies using stubs and shims ensures that you find an appropriate cause of a test failure. Stubs and shims are helpful to test parts of the code that are still to be implemented.
In this recipe, we are going to create tests for the code that has external dependencies either from the .NET class library or from inside our own code.
Before going ahead, it would be a good idea to create code that can be tested using the Microsoft Fakes framework. The following static
method checks whether the current date is December 21, 2012, and based on this, it will throw an exception:
public static void DoomsDay() { if(DateTime.Now.Equals(new DateTime(2012, 12, 21))) throw new Exception("Time's Up !!!! World will end now"); //Other code }
You can see that it is as a static
method but it has the DateTime.Now
static property tied to it, which makes it very hard to test the method and generate an exception. As the DateTime.Now
property is defined inside the .NET framework and will evaluate DoomsDay
only when the system date reaches that particular day, it will become mandatory to replace the call with some fake calls:
public static void DeleteTemporaryData(string dirLocation) { Directory.Delete(dirLocation, true); }
Similar to the preceding code, DeleteTemporaryData
will delete the entire directory that has been passed recursively.
We will create an interface ILogger
to implement the Logger
utility. To test the stubs, we will call the interface to create an external dependency:
public string GetEventName(ILogger logger) { if (logger.IsLoggerEnabled) logger.Log("GetEventName method is called"); return "Sample Event"; }
Here, the ILogger
interface has one Boolean property IsLoggerEnabled
and a Log
method to write the log entry from the logger.
Let's take a look at how we can create a Fakes project solution in the following steps:
MSFakesSample.Tests
.System.dll
. We can right-click on the normal assembly to create its Fake, as shown in the following screenshot:DoomsDay
method we created earlier. As a test case, we need to generate an exception, which is only intended for December 21, 2012. We use shims to define a Fake implementation of DateTime.Now
inside ShimsContext
:[TestMethod] [ExpectedException(typeof(Exception))] public void DoomsDay_Test_Shims() { using (ShimsContext.Create()) { ShimDateTime.NowGet = () => new DateTime(2012, 12, 21); DiagonizeShims.DoomsDay(); } }
In the preceding unit test, the Fakes assembly for the project automatically creates intercepts that can redefine the existing APIs. Here, inside ShimsContext
we redefine the Get
method of the property. Now using Delegate
, which returns a valid date, a call to the DoomsDay
method generates the expected exception.
DateTime.Now
is defined in mscorlib.dll
, which has been faked using the Fakes framework. You can also specify BehaveAsNotImplemented
to ensure that an API throws NotImplementedException
when it is called:[TestMethod] [ExpectedException(typeof(ShimNotImplementedException))] public void DeleteDirectory_BehaveNotImplemented() { using (ShimsContext.Create()) { ShimDirectory.BehaveAsNotImplemented(); DiagonizeShims.DeleteTemporaryData(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData)); } }
In the preceding code, DeleteTemporaryData
generates ShimNotImplementedException
because we defined the Fakes implementation of the Directory
class to BehaveAsNotImplemented
. Remember that the shims implementation will only work on the calls inside ShimsContext
.
GetEventName
method, we use a stub for the ILogger
interface so that it does not actually log data, rather it calls the custom test logger stubs:[TestMethod] public void GetEventName_Test() { //Arrange var sLogger = new StubILogger { IsLoggerEnabledGet = () => true }; var sut = new DiagonizeStubs(); //Act var result = sut.GetEventName(sLogger); //Assert Assert.IsFalse(string.IsNullOrEmpty(result)); }
In the preceding code, the test will call StubILogger
, which is a fake implementation of the ILogger
interface and is used instead of the actual Logger
class. The StubILogger
variable exposes properties that enable the tester to assign a delegate for the Get
and Set
methods of the properties and other methods associated with the interface. Here, we have changed the default implementation of the Get
method of IsLoggerEnabled
to return the true
value.
[TestMethod] public void GetEventName_Test_Fakes() { //arrange var stubLogger = new StubILogger { IsLoggerEnabledGet = () => true }; var diagonizeStub = new DiagonizeStubs(); var observer = new StubObserver(); stubLogger.InstanceObserver = observer; //act diagonizeStub.GetEventName(stubLogger); var calls = observer.GetCalls(); //assert Assert.IsNotNull(calls); Assert.AreEqual(2, calls.Length); }
Here, we have created an instance of StubObserver
, which is assigned to InstanceObserver
for the logger implementation. If you remember the actual definition of GetEventName
, it first checks the value of IsLoggerEnabled
before calling Log
. The preceding code will observe two calls: one to the IsLoggerEnabledGet
method and another to the LogString
method.
The Microsoft Fakes framework provides a superior technique to isolate a portion of the code from external sources. Shims and stubs are important picks to implement the level of isolation. Shims are used mainly to test the untestable code that comes with calls from third-party components with a lot of statics and privates. Shims rewrite the code completely during compilation to replace the actual calls during testing, and hence it requires a long time to load. Stubs, on the other hand, are recommended for virtual types, which are much faster with minimum or no code replacement during compile time.
In the following diagram of the component, the test uses the component that has the dependency of some external files (System.dll
) and some other components defined in the assembly. When an isolation technique is taken, the component replaces the calls to the external components with shim methods and other internal calls, using stubs.
Shim replaces all the calls to a method inside its context with a new implementation, if provided. For instance, if you take a closer look at the implementation of ShimDateTime.NowGet
in the preceding assembly using .NET Reflector, you will find that ShimRunTime
replaces DateTime
. The NowGet
property with the delegate that we have passed data to the TestMethod
is shown in the following code:
public static FakesDelegates.Func<DateTime> NowGet { [ShimMethod("get_Now", 24)] set { ShimRuntime.SetShimPublicStatic((Delegate) value, typeof(DateTime), get_Now", typeof(DateTime), new Type[0]); } }
In the preceding implementation of NowGet
, you can see that the delegate we passed to the property is replaced with the actual get_Now
method of the DateTime
object only within the current context. On the other hand, calling the BehaveAsNotImplemented()
method injects NotImplementedException
inside the actual definition of the methods that are defined in the Directory
class present inside the context. Thus, shims ensure that calls to any API are properly replaced to call a Fake assembly rather than the actual API call.
Unlike shims, stubs implement interfaces inside the Fake assembly. It ensures that all the methods (even the Get
and Set
methods of property) expose a matching property of the delegate type to enable the test environment to replace the default content when required. Shims also make calls to the observer to ensure that all the calls to the methods and properties are logged on the observer. If we see the implementation of StubILogger
, we can also see that there are delegates defined for each of the three methods as follows:
public FakesDelegates.Func<bool> IsLoggerEnabledGet; public FakesDelegates.Action<bool> IsLoggerEnabledSetBoolean; public FakesDelegates.Action<string> LogString;
The delegates passed to these methods will be called when the actual methods are called from the test environment.
18.224.59.145