Introduction
Sometimes you must do some changes on legacy programs that were not built using good development practices. At that time, our first reaction is to run away from that: the probability of having problems is too high and touching the source code is almost impossible - we are working on a castle of cards, where even the slightest move can bring everything down.
The idea of refactoring (or even rewrite) the code comes from a long time, but where is the time to do that? All projects are late and that's not a priority, its working, no? Why touch on that?
We can make the situation a little better by creating unit tests for the changes we must do, but how to do that? The program is full of dependencies, the business rules are mixed with the views, and the methods are long and complicated.
The best we could do are integration tests, that are difficult to implement and slow to execute, they will be abandoned very soon. What to do now? The solution is at hand, Microsoft implemented a Microsoft Research's project, named Moles, and put it in Visual Studio 2012 with the name of Fakes, so we can create test for anything, including .Net Framework classes.
With Fakes we can create unit tests that run fast and don't depend of anything, nor operating system, machine or database. Here I must do a note: although Fakes can test almost anything, that's not an excuse to relax our coding style - you must use good programming practices on the new code and do not rely on Fakes to simulate dependencies that should not exist. When you are creating new code, try to do it in a way that you don't need Fakes, use it only where that is really needed.
The problem
To show how to use Fakes, we will create a small WPF project that has a window with a TextBlock:
<Window x:Class="FakesTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TextBlock x:Name="TxtDate"/>
</Grid>
</Window>
When the window is initialized, the TextBlock is fed with the current date:
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
TxtDate.Text = string.Format(
"The current date is {0:d}", DateTime.Now);
}
}
We should change this code to put the weekday in the TextBlock, like in "The current date is 8/2/2013 (Friday)". We want to use best practices and will create unit tests for the project. But how to do it? The code is mixed with the View and has a dependency on DateTime, a .Net Framework class.
Creating the test project
The first step is to create the test project. On the Solution Explorer, select the solution and right click it, selecting Add/New Project. Select Test/Unit Test Project and call it FakesTest.Tests. Add to it a reference to our project. Rename the UnitTest1.cs class to MainWindowTests.cs.
Here starts our problem: we want to test what is being shown in the TextBlock. For that, we must create a window, initialize it and look at its content. Not an easy task to do with unit tests.
Here comes Fakes to rescue. On the test project, on the references, select the reference to the main project and select Add Fakes Assembly. A new reference to FakesTest.Fakes is added - this reference will create all fakes for the window and its componentes. To use it on our tests, we must add the references to System.Xaml, PresentationCore, PresentationFrameworkand WindowsBase.
The next step is to create a fake for the DateTime class. Select the System assembly in the references and right click it, selecting Add Fakes Assembly. Now that we have all our environment "virtualized", we can start creating tests.
To use the components created by Fakes we must surround the calls in a context, with the keyword Using, using a ShimsContext:
using (ShimsContext.Create())
{
….
}
Inside the context, we must tell the behavior of the fake DateTime class, when the method Now will be called:
ShimDateTime.NowGet = () => new DateTime(2013,1,1);
To access the DateTime class created by Fakes, we must use the ShimDateTime class. The line above tells us that when the getter of the Now property is called (NowGet), it must return 1/1/2013. From now on, the test becomes exactly the same as any other unit test:
[TestMethod]
public void CreateWindow_TextBlockHasCurrentDate()
{
using (ShimsContext.Create())
{
ShimDateTime.NowGet = () => new DateTime(2013,1,1);
var window = new MainWindow();
var mainGrid = (Grid)window.Content;
var textBlock = (TextBlock) mainGrid.Children[0];
Assert.AreEqual("The current date is 01/01/2013",textBlock.Text);
}
}
We have created the window and verified if the TextBlock has the date we have passed on the fake. When we execute the tests with Ctrl+R+T, we see that the test passes. We are covered here, and we can make the changes we need on our code.
Changing the program
As we already have the test, we can change it for the new feature we want:
[TestMethod]
public void CreateWindow_TextBlockHasCurrentDate()
{
using (ShimsContext.Create())
{
ShimDateTime.NowGet = () => new DateTime(2013,1,1);
var window = new MainWindow();
var mainGrid = (Grid)window.Content;
var textBlock = (TextBlock) mainGrid.Children[0];
Assert.AreEqual(
"The current date is 01/01/2013 (Tuesday)",textBlock.Text);
}
}
We have made the change on the test but not on the code. If we run our test now, it will fail:
Assert.AreEqual failed. Expected:<The current date is 01/01/2013 (Tuesday)>. Actual:< The current date is 01/01/2013>.
We must change the code:
public MainWindow()
{
InitializeComponent();
TxtDate.Text = string.Format(
"The current date is {0:d} ({0:dddd})", DateTime.Now);
}
When we execute the test, it passes - our change is complete and working. To complete the Red-Green-Refactor cycle from TDD, we can refactor the code, extracting the function from where we set the TextBlock:
public MainWindow()
{
InitializeComponent();
TxtDate.Text = GetTextDate();
}
private static string GetTextDate()
{
return string.Format(
"The current date is {0:d} ({0:dddd})", DateTime.Now);
}
We run the tests and they still pass. The change is finished.
Conclusions
As we can see, Fakes helps a lot when we have legacy code that, for its dependencies, don't allow creating unit tests in an easy way. What we have done here is to simulate the creation of a window and assign a value to a TextBlock, passing the current date with no special arrangement.
We are not advocating using this resource always - you should use Fakes only as the last resource, when the code is already there and there is not an easy way to break it. It can be used as a starting point for the real refactoring of the program: in this case, we must remove the window dependencies using the MVVM pattern and also from the System.DateTime, using some kind of wrapper interface (that we could call as IDateTimeProvider), but this is a subject for another article!
1 thought on “Testing the untestable with Microsoft Fakes”