Monday, 22 July 2013

Isolate your tests from dependencies with TypeMock’s Isolator

Software development process is continuously evolving. One of the most talked practices in current era is Test Driven Development (TDD). There can’t be a day when I get into twitter and not seen some tweets on TDD and Unit testing. After some days, we may see very less number of projects that don’t have unit tests written.

If a piece of code is driven by tests, the code will not only have less bugs but the developers also spend some time to refactor and make the code cleaner. This makes the job of future developers easier, as they don’t have to take pain to write unit tests for a small piece that they write to add a new feature to the existing giant code base.

But we have a huge number of legacy projects for which we don’t have unit tests written. As most of such projects were not written with layers and abstractions, one will be more than scared to write automated unit tests for them. But these projects shouldn’t be left behind. A mocking framework with capability to mock out tight dependencies will make the work easier.

TypeMock’s Isolator is a right choice to pick in such scenarios. There are three versions of Isolator; the details on these versions are available on the product page.

To start using Isolator in a unit test project, a couple of assembly references have to be added. These dlls are added to GAC once you install Isolator. In C# projects, we need the references that are highlighted in the following screenshot.

In VB.NET projects, a reference to Typemock.Isolator.VisualBasic has of be added. These namespaces have to be included in the unit test class and the test methods have to be decorated with Isolate attribute as shown:

[TestMethod, Isolated]
public void Should_Set_value_To_Orders()


This attribute is used to keep each test method independent of each other.

Isolator has a uniform interface for mocking. Meaning, the API syntax to mock abstract types and concrete types is the same. Let us see how Isolator makes the process of writing unit tests easier in action.

Mocking interfaces
Let’s consider the following interface:

public interface IOrdersView
        IEnumerable<Order> Orders { get; set; }
        string this[string key] {get;}
        event EventHandler<EventArgs> LoadOrders;

It is a very generic interface which will be implemented by a view and can be used in any type of application. Let’s assume that it is used in an ASP.NET application following Model-View-Presenter pattern. The presenter will hold a reference of this interface type. While writing tests for the presenter, we need a mock object of this interface. Following statement creates the mock for us:

IOrdersView ordersView = Isolate.Fake.Instance<IOrdersView>();

Let’s fake behaviour of each of the component now. Let’s start with the property Orders.

Isolate.WhenCalled(()=>ordersView.Orders).WillReturn(new List<Order>(){
    //Use collection initializer to set values

As we see, it is quite easy to skip mock implementation and return some custom values. The same API can be used to mock a method as well. Following statement fakes the event load orders.

Isolate.Invoke.Event(() => ordersView.LoadOrders += (s, e) => { 
     //A custom mock implementation goes here

It is not necessary to implement a handler; it can be skipped by assigning null to the event handler. If the event accepts parameters, they can be passed after the mock implementation.

Faking an indexer is also similar to faking methods. While faking a method accepting parameters or indexers, we must specify value of the argument. Following statement is used to return a custom value when the indexer is asked to provide value of ID:

Isolate.WhenCalled(() => orders["ID"]).WillReturn("1");

Mocking Concrete types
Most of the legacy applications don’t follow rules of separation of concerns that we talk these days. For instance, if we take an old ASP.NET web forms based application, the code behind of each of the form is very heavily loaded with code. Testing such classes is not easy. Most scary object to deal with in ASP.NET code behind is HttpContext. This single object has a bunch of properties that are set by the web server when the application starts. Isolator’s API has a method that fakes all the properties in one go.

Isolate.WhenCalled(() => HttpContext.Current).ReturnRecursiveFake();

If there is a need to return some specific values when certain properties are invoked, you can do that using same API that we used for mocking interfaces. It is shown below:

var httpContextFake = Isolate.Fake.Instance<HttpContext>();
Isolate.WhenCalled(() => httpContextFake.Session["UserId"]).WillReturn("U001");
Isolate.WhenCalled(() => HttpContext.Current).WillReturn(httpContextFake);

Many applications contain a data access layer that is responsible to hit the database to operate on data from the application. Before technologies like Entity Framework came in, most of the projects used to perform database operations using ADO.NET. While writing tests for logic involving objects of SqlConnection and SqlCommand, we must prevent the tests from hitting the database. Isolator makes it possible to replace the actual objects with mocked objects on the fly. Following snippet demonstrates it:

var sqlCommandInstance = Isolate.Fake.Instance<SqlCommand>();

The first statement that creates the fake instance of SqlCommand mocks all methods defined in the class as well. After these statements, if we instantiate the data access layer and call a method that uses ExecuteNonQuery, then the call won’t hit the physical database. It will invoke the fake method that is just created.

These are some of the most common scenarios that we face during unit testing. Isolator is capable of doing much more than what is discussed here. Some of the eye-catching features include mocking:

  1. LINQ queries
  2. Private members
  3. Static members
  4. Counting calls
  5. Extension methods

To learn more on Typemock’s Isolator, you may visit the product page and checkout the online documentation.

Happy coding!

No comments:

Post a Comment

Note: only a member of this blog may post a comment.