TDD: Introduction to Typemock Isolator

Typemock Isolator is often referred to the most powerful Mock Object Framework for .NET. I’ve heard people talk about it as “The Big Guns”, “The Nuclear Weapon”, the “Cleanup Guy”. In this blog entry, I’m going to provide you with a brief introduction to Typemock Isolator.

In the first section, I’m going to explain the purpose of Mock Object Frameworks and why you should care about them (ASP.NET MVC developers, pay attention). Next, I’m going to provide you with some toy-simple samples of using TypeMock Isolator so you get the idea of how to use the framework. One of the samples demonstrates how you can use Typemock to simulate LINQ to SQL queries (very, very cool stuff). Finally, I’m going to tackle a more realistic sample of using Typemock. In the final section of this blog entry, I’m going to show you how you can use Typemock to mock configuration and database dependencies so that you can write effective unit tests.

The Importance of Mocking

Typemock Isolator is a Mock Object Framework (also referred to simply as a Mocking Framework). A Mock Object Framework enables you to simulate objects when testing your code. For example, you can use Typemock to simulate instantiating objects, calling object methods, and reading and setting object properties.

Why is this important? If you are practicing Test-Driven Development (TDD), then you write unit tests like a maniac. Practitioners of TDD follow a process called Red, Green, Refactor. When you sit down and start writing an application, the first thing you do is write a test. Then you run the test and it fails because you haven’t written any application code (this is the Red part). Next, you write the necessary code to pass the test (and only the necessary code to write the test). When you run the test again, you pass (this is the Green part). Next, you might cleanup your code. For example, you might consolidate common logic into a single method (this is the Refactor part). Repeat, repeat, repeat.

If you are building ASP.NET MVC applications then, most likely, you’ll also be practicing TDD. One of the main motivations for building ASP.NET MVC is to support TDD. The MVC architecture enables a clean Separation of Concerns (SoC) which makes Test-Driven Development possible.

When writing code using TDD, you constantly write and run unit tests. You run these tests over and over and over again. This means that a unit test must be fast. If your unit tests are not fast, then you’ll never get any work done because you’ll be sitting around all day waiting for your unit tests to complete.

Furthermore, your unit tests should enable you to test a particular piece of code (a unit of code) in isolation. If one unit of code is dependent on another unit of code, then you end up not testing a single unit of code. If your code is riddled with dependencies, then your unit tests quickly turn into what are called integration or system tests. You end up testing an entire application with a unit test instead of particular unit of code.

So, unit tests need to be fast and they need to enable you to test code in isolation. In practice, satisfying these requirements can be very difficult. The problem is that real-world code often has many dependencies. What is worse, often these dependencies are dependencies on external resources such as databases, web services, or the file system.

The Problem

Imagine, for example, that you are building a database-driven web application. You are building an online store. Imagine that the application currently has a method that retrieves all of the products from the database that looks like this:

        public static List<Product> GetProducts()
        {
            string conString = ConfigHelper.GetDBConnectionString();
            DataProvider dp = new DataProvider(conString);
            return dp.GetProducts();
        }

The GetProducts() method gets a database connection string from a class named ConfigHelper, instantiates a data provider, and calls the data provider’s GetProducts() method to grab the products from the database.

Imagine that you are asked to add a new feature to this application. When retrieving products, you are asked to calculate the sales tax. You are asked to modify the method that retrieves the products from the database in such a way that the method adds the sales tax to each product’s price. You are asked to make the new method look like this:

        public static List<Product> GetProducts(decimal tax)
        {
            ... modified code that adds sales tax ...
        }

Following good TDD practice, you start implementing this feature by writing a unit test. But here is where you start encountering problems. The GetProducts() method has two external dependencies. First, the method uses a class called ConfigHelper to get the database connection string. The ConfigHelper class reads the connection string from the Web.config file.

You don’t want to read the database connection string from the web.config file whenever you run the test. Reading information from a configuration file violates the requirement that a unit test should test code in isolation. You don’t want to test whether or not the configuration file is setup correctly. Instead, you want to test your tax calculation.

Second, the GetProducts() method has a dependency on a DataProvider object. The DataProvider retrieves a set of records from the database. This dependency violates both the isolation and the speed requirements. If you call the GetProducts() method in your unit test, then you will access the database, and that can make the unit test slow (Imagine that you have 500 of these unit tests that you execute whenever you change the code in your application).

At this point, you might conclude that there is no way to unit test the GetProducts() method. Units of code are an illusion since all real code is interdependent. No code is an island. Therefore, unit tests are impossible. Therefore, TDD is a foolish practice. Therefore, ASP.NET MVC is a stupid framework. Therefore, we should write all of our websites in Perl.

The Solution

Please don’t start using Perl. The solution to the problem discussed in the previous section is to use a Mock Object Framework. A Mock Object Framework enables you to simulate objects. You can use mock objects to trick your application into thinking that it is interacting with the normal objects when, in fact, it is actually interacting with sneaky imposters.

In the case of the GetProducts() method, you can use a Mock Object Framework to simulate both the ConfigHelper and the DataProvider objects. You can create a mock ConfigHelper class that doesn’t really access the web.config file. You can create a mock DataProvider class that doesn’t really access the database. That way, you can test your tax calculation code in isolation.

What makes Mock Object Frameworks, such as Typemock Isolator, truly powerful is that you can even return fake data from your mock object methods and properties. For example, you can cause the mock DataProvider.GetProducts() method to return a fake collection of products each time it is called. That way, you can test your tax calculation code against a set of products without actually accessing the database.

In summary, Mock Object Frameworks are critical tools for Test-Driven Development. If you are interested in using ASP.NET MVC, and you want to pursue Test-Driven Development, then it is a good idea to get familiar with the Mock Object Frameworks (this is the rationale for this blog entry).

Using Typemock Isolator

Typemock Isolator is a commercial product. As far as I know, it is the only commercial Mock Object Framework for the .NET framework. Typemock Isolator comes in a (free) Community Edition, a Professional edition, and an Enterprise Edition. You can download Typemock Isolator from the following URL:

http://www.typemock.com/Downloads.php

There is only one download. You get the Professional and Enterprise features for 30 days and then, if you don’t pay for a license, Typemock downgrades to the hobbled Community Edition. I am using the full version of Typemock for the purposes of this blog entry.

After you download and install Typemock, you can use it when building unit tests by adding a reference to Typemock Isolator for .NET 2.0 in your Visual Studio test project. Select the menu option Project, Add Reference and then select the .NET tab and type the letter ‘t’ (see Figure 1). You use the Typemock Isolator for .NET 2.0 reference for both .NET Framework 2.0 and .NET Framework 2.5 applications (Typemock has really good support for .NET Framework 3.5).

clip_image002

Figure 1 – Adding a reference to Typemock Isolator

Mocking an Object

Let’s start with a simple sample of using Typemock. Imagine that you have the class in Listing 1:

Listing 1 – MyClass.cs

   1:  using System;
   2:   
   3:  namespace MyProject
   4:  {
   5:      public class MyClass
   6:      {
   7:          public MyClass()
   8:          {
   9:              throw new Exception("Hello from constructor!");
  10:          }
  11:      }
  12:  }

The class in Listing 1 is a weird class. If you attempt to create an instance of the class, then an exception is thrown. I’m using this weird class because I want to demonstrate that when you use Typemock, you are working with a simulation of a class and not the class itself. We’ll be able to create an instance of the MyClass class in our code without throwing an exception by mocking the class.

The test in Listing 2 uses Typemock to create an instance of the MyClass class.

Listing 2 – MyClassTest.cs

   1:  using System;
   2:  using System.Text;
   3:  using System.Collections.Generic;
   4:  using System.Linq;
   5:  using Microsoft.VisualStudio.TestTools.UnitTesting;
   6:   
   7:  using TypeMock;
   8:  using MyProject;
   9:   
  10:  namespace TestTypeMockTests
  11:  {
  12:   
  13:      [TestClass]
  14:      public class MyClassTest
  15:      {
  16:          [TestMethod]
  17:          public void Constructor_Call_Success()
  18:          {
  19:              // Expectations
  20:              using (RecordExpectations expect = RecorderManager.StartRecording())
  21:              {
  22:                  MyClass cls = new MyClass();
  23:              }
  24:   
  25:              // Reality
  26:              MyClass newClass = new MyClass(); // success
  27:              
  28:              //MyClass newClass2 = new MyClass(); // failure
  29:   
  30:              // Verify
  31:              MockManager.Verify();
  32:          }
  33:   
  34:      }
  35:  }

Let me explain what is going on in Listing 2. First, notice that I have imported the Typemock namespace as the top of the file. I need this namespace to use the Typemock classes.

The Constructor_Call_Success() method is used to test the MyClass class. This method tests whether an instance of MyClass can be created. The mock objects are used within this test method.

When using Typemock, you typically have three sections of code. In the first section, you setup your expectations. In the second section, you test your expectations against reality. In the final section, you verify that expectations and reality match.

Notice the using block. This block contains the expectations section. Anything that you do inside of this using block doesn’t actually happen. When the instance of MyClass is created, it is not actually created. The purpose of the expectation section is to setup the mock objects and record what you expect to happen to the mock objects. In Listing 2, we are recording our expectation that the MyClass class will be instantiated.

The next section, the section after the using block, is the reality section. This section contains the actual unit test code. In Listing 2, an instance of MyClass is created. Again, the actual MyClass is not created. Instead, the mock MyClass imposter is created. You setup the mock MyClass in the expectations section.

An exception is not thrown when you create an instance of MyClass. Since you are working with a mock object instead of the actual object, the normal MyClass constructor code is never executed.

Finally, you call the MockManager.Verify() method to make sure that reality meets your expectations. In the expectations section, you setup the expectation that an instance of MyClass would be created. If an instance of MyClass was not, in fact, created, then the MockManager.Verify() method would fail and the unit test would fail.

The reality section in Listing 2 includes one line of code that has been commented out. This line of code creates a second instance of MyClass. If you uncommented this line of code, then the MockManager.Verify() method would fail. It would fail because you setup the expectation, in the expectations section, that the MyClass class would only be instantiated once.

Mocking a Method Call

You can mock a method call – either static or instance methods – by following the same procedure described in the previous section. You setup the expectations, execute your test code, and verify that your expectations match the reality generated by your test code.

For example, Listing 3 contains a slightly modified version of the MyClass class.

Listing 3 – MyClass.cs (modified with new DoSomething() method)

   1:  using System;
   2:   
   3:  namespace MyProject
   4:  {
   5:      public class MyClass
   6:      {
   7:          public void DoSomething()
   8:          {
   9:              throw new Exception("Hello from DoSomething!");
  10:          }
  11:   
  12:          public MyClass()
  13:          {
  14:              throw new Exception("Hello from constructor!");
  15:          }
  16:      }
  17:  }

The modified MyClass in Listing 3 includes a new method named DoSomething(). If you call DoSomething(), an exception is thrown. We can mock calling the DoSomething() method with the test in Listing 4.

Listing 4 – MyClassTest.cs

   1:  using System;
   2:  using System.Text;
   3:  using System.Collections.Generic;
   4:  using System.Linq;
   5:  using Microsoft.VisualStudio.TestTools.UnitTesting;
   6:   
   7:  using TypeMock;
   8:  using MyProject;
   9:   
  10:  namespace TestTypeMockTests
  11:  {
  12:   
  13:      [TestClass]
  14:      public class MyClassTest
  15:      {
  16:   
  17:          [TestMethod]
  18:          public void DoSomething_Call_Success()
  19:          {
  20:              // Expectations
  21:              using (RecordExpectations expect = RecorderManager.StartRecording())
  22:              {
  23:                  MyClass cls = new MyClass();
  24:                  cls.DoSomething();
  25:              }
  26:   
  27:              // Reality
  28:              MyClass newClass = new MyClass(); 
  29:              newClass.DoSomething(); // success
  30:              // newClass.DoSomething(); // failure
  31:   
  32:              // Verify
  33:              MockManager.Verify();
  34:          }
  35:      }
  36:  }

When you execute the test in Listing 4, an exception is not thrown, and the test succeeds. The actual DoSomething() method is not called. Instead, the method call is mocked.

In the expectations section, the expectation is setup that the DoSomething() method will be called once and only once. If the DoSomething() method is not called at all, then the MockManager.Verify() method will fail. Calling the DoSomething() method more than once, also will cause the MockManager.Verify() method to fail.

Mocking Method Parameters

You also can mock methods that accept parameters. You can even set expectations about the values that you expect for the parameters. Listing 5 contains a modified version of the MyClass class with a new version of the DoSomething() method that accepts a string and an integer parameter.

Listing 5 – MyClass.cs (with new DoSomething() method that accepts parameters)

   1:  using System;
   2:   
   3:  namespace MyProject
   4:  {
   5:      public class MyClass
   6:      {
   7:          public void DoSomething(string param1, int param2)
   8:          {
   9:              throw new Exception("Hello from DoSomething!");
  10:          }
  11:   
  12:   
  13:          public MyClass()
  14:          {
  15:              throw new Exception("Hello from constructor!");
  16:          }
  17:      }
  18:  }

The test in Listing 6 sets up some expectations concerning the parameters. In the expectations section, the DoSomething() method is called twice. After the DoSomething() method is called the second time, the CheckArguments() method is called.

Listing 6 – MyClassTests.cs

   1:  using System;
   2:  using System.Text;
   3:  using System.Collections.Generic;
   4:  using System.Linq;
   5:  using Microsoft.VisualStudio.TestTools.UnitTesting;
   6:   
   7:  using TypeMock;
   8:  using MyProject;
   9:   
  10:  namespace TestTypeMockTests
  11:  {
  12:   
  13:      [TestClass]
  14:      public class MyClassTest
  15:      {
  16:   
  17:          [TestMethod]
  18:          public void DoSomething_Call_Success()
  19:          {
  20:              // Expectations
  21:              using (RecordExpectations expect = RecorderManager.StartRecording())
  22:              {
  23:                  MyClass cls = new MyClass();
  24:                  cls.DoSomething("monday", 2);
  25:                  cls.DoSomething("tuesday", 42);
  26:                  expect.CheckArguments();
  27:              }
  28:   
  29:              // Reality
  30:              MyClass newClass = new MyClass();
  31:              newClass.DoSomething("saturday", 16); // any parameters
  32:              newClass.DoSomething("tuesday", 42); // matching parameters
  33:   
  34:              // Verify
  35:              MockManager.Verify();
  36:          }
  37:   
  38:   
  39:   
  40:      }
  41:  }

Since the CheckArguments() method is not called after the first DoSomething() call, no expectations are setup concerning the parameters. The parameters just act as dummy parameters. Later in the test section, any values can be passed for the parameters.

Since the CheckArguments() method is called after the second DoSomething() call, expectations are setup concerning these parameters. When the test is performed, the exact values “tuesday” and 42 must be passed to the DoSomething() method.

In the reality section, the test is passed. The first time the DoSomething() method is called, you can call the method with any parameter values that you feel like. The second time the method is called, you must pass the two parameter values “tuesday” and 42.

If you want to get really fancy, you can even setup parameter constraints. For example, you can require that a parameter contain a particular substring. Take a look at the documentation installed with Typemock to learn how to create richer parameter constraints.

Mocking a Return Value

You also can mock method return values. This is useful when you need to feed a value into the rest of your code to test. For example, the modified version of MyClass in Listing 7 contains a method named GetDBConnectionString(). This method reads the connection string from the web.config file.

Listing 7 – MyClass.cs (modified with GetDBConnectionString() method)

   1:  using System;
   2:  using System.Configuration;
   3:  using System.Web.Configuration;
   4:   
   5:  namespace MyProject
   6:  {
   7:      public class MyClass
   8:      {
   9:          public string GetDBConnectionString()
  10:          {
  11:              ConnectionStringSettings settings = WebConfigurationManager.ConnectionStrings["con"];
  12:              if (settings == null)
  13:                  throw new ConfigurationErrorsException("Missing connection string con");
  14:              return settings.ConnectionString;
  15:          }
  16:   
  17:      }
  18:  }

When performing a unit test, you don’t want to read from the configuration file. Reading from a configuration file adds external dependencies to your unit tests. Instead, you should mock the GetDBConnectionString() method and just return a hard-coded value. Listing 8 illustrates how you can do this.

Listing 8 – MyClassTest.cs

   1:  using System;
   2:  using System.Text;
   3:  using System.Collections.Generic;
   4:  using System.Linq;
   5:  using Microsoft.VisualStudio.TestTools.UnitTesting;
   6:   
   7:  using TypeMock;
   8:  using MyProject;
   9:   
  10:  namespace TestTypeMockTests
  11:  {
  12:   
  13:      [TestClass]
  14:      public class MyClassTest
  15:      {
  16:   
  17:          [TestMethod]
  18:          public void GetDBConnectionString_Call_ReturnsDSN()
  19:          {
  20:              // Expectations
  21:              using (RecordExpectations expect = RecorderManager.StartRecording())
  22:              {
  23:                  MyClass cls = new MyClass();
  24:                  cls.GetDBConnectionString();
  25:                  expect.Return("DSN=myData");
  26:              }
  27:   
  28:              // Reality
  29:              MyClass newClass = new MyClass();
  30:              string conString = newClass.GetDBConnectionString();
  31:   
  32:              Assert.AreEqual("DSN=myData", conString);
  33:          }
  34:      }
  35:  }

Notice that the expectations section in Listing 8 includes the following statement:

expect.Return(“DSN=myData”)

This statement causes the GetDBConnectionString(), when it is later called in the test section, to return the value “DSN=myData”.

If you don’t know how many times the GetDBConnectionString() method will be called in your test code, and you want a certain value to be returned whenever the method is called, then you can call the RepeatAlways() method in the expectations section like this:

    
                MyClass cls = new MyClass();
                cls.GetDBConnectionString();
                expect.Return("DSN=myData");
                expect.RepeatAlways();

There is one other thing that you should notice about Listing 8. You should notice that MockManager.Verify() is never called. We aren’t interested in verifying that a certain method was called in Listing 8. Instead, we just want to mock a return value so we can carry out some unit test.

Mocking Interfaces and Abstract Classes

You also can use Typemock to mock interfaces and abstract classes without actually implementing the interface or abstract class. This feature is important for Test-Driven Development because it allows you to develop your code in a piecemeal fashion. For example, imagine that your code depends on a data access layer that you have not written. If you create an interface for the yet to be created data access layer, you can use Typemock to mock a class that implements the interface. This allows you to delay writing the interface until the future.

For example, imagine that you have created the following interface:

   1:  using System;
   2:  using System.Collections;
   3:   
   4:  public interface IData
   5:  {
   6:      IEnumerable GetCustomers();
   7:   
   8:  }

The IData interface has one method named GetCustomers() that, when implemented, should return a list of customers. Here’s a quick sample of how you can mock this interface with Typemock Isolator:

   1:  using System;
   2:  using System.Text;
   3:  using System.Collections;
   4:  using System.Linq;
   5:  using Microsoft.VisualStudio.TestTools.UnitTesting;
   6:  using TypeMock;
   7:   
   8:  namespace TestTypeMockTests
   9:  {
  10:      [TestClass]
  11:      public class InterfaceTest
  12:      {
  13:   
  14:          private ArrayList _fakeCustomers = new ArrayList
  15:              {
  16:                  new {Id=1, Name="Stephen Walther"},
  17:                  new {Id=3, Name="Sue Jones"},
  18:                  new {Id=8, Name="Ruth Walther"}
  19:              };
  20:   
  21:          [TestMethod]
  22:          public void GetCustomers()
  23:          {
  24:              // Create interface stub
  25:              IData data = RecorderManager.CreateMockedObject<IData>();
  26:   
  27:              // Expectations
  28:              using (RecordExpectations expect = RecorderManager.StartRecording())
  29:              {
  30:                  data.GetCustomers();
  31:                  expect.Return(_fakeCustomers);
  32:              }
  33:   
  34:              // Reality
  35:              ArrayList results = (ArrayList)data.GetCustomers();
  36:              Assert.AreEqual(3, results.Count);
  37:          }
  38:   
  39:      }
  40:  }

The first statement in the GetCustomers() test method is the most important one to notice. The CreateMockedObject() method creates a stub class that implements the IData interface. In the expectations section, this stub class is setup so that it returns a fake list of customers when its GetCustomers() method is called.

After you create the mock implementation of the interface, you can pretend that you have already done the work of implementing it. Notice that the mock implementation can be used just fine in the reality section of the test.

Mocking LINQ to SQL Queries

One of the coolest features of Typemock is its support for mocking LINQ to SQL queries. You can hardcode a particular value that is returned whenever a LINQ to SQL query is performed. For example, the modified MyClassTest class in Listing 9 substitutes a fake static collection of products for the normal database results that would be returned from a LINQ to SQL query.

Listing 9 – MyClass.cs (with GetProducts() method)

   1:  using System;
   2:  using System.Text;
   3:  using System.Collections.Generic;
   4:  using System.Linq;
   5:  using Microsoft.VisualStudio.TestTools.UnitTesting;
   6:   
   7:  using TypeMock;
   8:  using MyProject;
   9:   
  10:  using System.Data.Linq;
  11:   
  12:  namespace TestTypeMockTests
  13:  {
  14:   
  15:      [TestClass]
  16:      public class MyClassTest
  17:      {
  18:   
  19:          private IQueryable _fakeProducts = new List<Product>
  20:              {
  21:                  new Product {Id=1, Name="Steak", Price=12.23m},
  22:                  new Product {Id=3, Name="Cheese", Price=2.00m},
  23:                  new Product {Id=8, Name="Milk", Price=5.39m}
  24:              }.AsQueryable();
  25:   
  26:   
  27:          [TestMethod]
  28:          public void GetProducts_Call_ReturnProducts()
  29:          {
  30:   
  31:              using (RecordExpectations recorder = RecorderManager.StartRecording())
  32:              {
  33:                  ProductsDataContext mockDB = new ProductsDataContext();
  34:                  var mockProducts = mockDB.Products;
  35:                  recorder.ReturnDefaultImplementation();
  36:                  var query = from p in mockProducts select p;
  37:                  recorder.Return(_fakeProducts);
  38:              } 
  39:   
  40:   
  41:              // Test
  42:              ProductsDataContext db = new ProductsDataContext();
  43:              var products = from p in db.Products select p;
  44:   
  45:              // 3 products in all
  46:              Assert.IsTrue(products.Count() == 3); 
  47:              // First product is steak
  48:              var lstProducts = products.ToList();
  49:              Assert.AreEqual(lstProducts[0].Name, "Steak");
  50:          }
  51:   
  52:   
  53:      }
  54:  }

When you execute the test in Listing 9, the return value of a particular LINQ to SQL query is mocked. When you perform the query from p in mockProducts select p, the contents of the _fakeProducts collection are returned.

The test section demonstrates that the fake set of products is returned. The count of products is always 3 regardless of the number of products in the database. The name of the first product is always “steak”.

Mocking a More Realistic Code Sample

In this final section, I tackle one last sample of mocking with Typemocks. I want to return to the original scenario discussed in the introduction of this blog entry.

Imagine that you are building an online store. Imagine that the code in Listing 10 represents your business logic layer and the code in Listing 11 represents your data access layer.

Listing 10 – BLL.cs

   1:  using DAL;
   2:  using System.Collections.Generic;
   3:  using System.Configuration;
   4:  using System.Web.Configuration;
   5:   
   6:   
   7:  namespace BLL
   8:  {
   9:      public class Product
  10:      {
  11:          public int Id { get; set; }
  12:   
  13:          public string Name { get; set; }
  14:   
  15:          public decimal Price { get; set; }
  16:   
  17:          public static List<Product> GetProducts()
  18:          {
  19:              string conString = ConfigHelper.GetDBConnectionString();
  20:              DataProvider dp = new DataProvider(conString);
  21:              return dp.GetProducts();
  22:          }
  23:      }
  24:   
  25:      public class ConfigHelper
  26:      {
  27:          public static string GetDBConnectionString()
  28:          {
  29:              ConnectionStringSettings settings = WebConfigurationManager.ConnectionStrings["con"];
  30:              if (settings == null)
  31:                  throw new ConfigurationErrorsException("Missing connection string con");
  32:              return settings.ConnectionString;
  33:          }
  34:      }
  35:   
  36:  }

Listing 11 – DAL.cs

   1:  using System.Data;
   2:  using System.Data.SqlClient;
   3:  using System.Collections.Generic;
   4:  using BLL;
   5:   
   6:  namespace DAL
   7:  {
   8:      public class DataProvider
   9:      {
  10:          private string _connectionString;
  11:   
  12:          public DataProvider(string dbConnectionString)
  13:          {
  14:              _connectionString = dbConnectionString;
  15:          }
  16:   
  17:          public List<Product> GetProducts()
  18:          {
  19:              SqlConnection con = new SqlConnection(_connectionString);
  20:              string cmdText = "SELECT Id,Name,Price FROM Products";
  21:              SqlCommand cmd = new SqlCommand(cmdText, con);
  22:              List<Product> results = null;
  23:              using (con)
  24:              {
  25:                  con.Open();
  26:                  SqlDataReader reader = cmd.ExecuteReader();
  27:                  while (reader.Read())
  28:                  {
  29:                      Product product = new Product();
  30:                      product.Id = (int)reader["Id"];
  31:                      product.Name = (string)reader["Name"];
  32:                      product.Price = (decimal)reader["Price"];
  33:                      results.Add(product);
  34:                  }
  35:              }
  36:              return results;
  37:          }
  38:      }
  39:  }

I’m not claiming that the code in Listing 10 and Listing 11 is well written code. Just imagine that this is legacy code that you are required to maintain.

Now, one fine day, you are asked to modify the GetProducts() method in Listing 10 so that it includes a tax calculation. You are asked to modify the signature of the method so that it looks like this:

        public static List<Product> GetProducts(decimal tax)
        {
            ... modified code that adds sales tax ...
        }

Following good Test-Driven Development methodology, before you do anything else, you write a test. This is where you need a Mock Object Framework like Typemock. In order to test the GetProducts() method, you need to mock both the ConfigHelper and DataProvider classes. If you don’t mock these objects, you’ll access your application’s configuration file and the database every time you run your tests.

The test in Listing 12 contains a test that checks whether the right tax calculation is being performed.

Listing 12 – ProductTest.cs

   1:  using System;
   2:  using System.Text;
   3:  using System.Collections.Generic;
   4:  using System.Linq;
   5:  using Microsoft.VisualStudio.TestTools.UnitTesting;
   6:  using TypeMock;
   7:   
   8:  using BLL;
   9:  using DAL;
  10:   
  11:  namespace TestTypeMockTests
  12:  {
  13:      [TestClass]
  14:      public class ProductTest
  15:      {
  16:   
  17:   
  18:          private List<Product> _fakeProducts = new List<Product>
  19:              {
  20:                  new Product {Id=1, Name="Steak", Price=12.23m},
  21:                  new Product {Id=3, Name="Cheese", Price=2.00m},
  22:                  new Product {Id=8, Name="Milk", Price=5.39m}
  23:              };
  24:   
  25:          [TestMethod]
  26:          public void GetProducts_Tax_Match()
  27:          {
  28:              // Expectations
  29:              using (RecordExpectations expect = RecorderManager.StartRecording())
  30:              {
  31:                  // Mock ConfigHelper
  32:                  ConfigHelper.GetDBConnectionString();
  33:                  expect.Return("dummy");
  34:                  
  35:                  // Mock DataProvider
  36:                  DataProvider mockDP = new DataProvider("dummy");
  37:                  mockDP.GetProducts();
  38:                  expect.Return(_fakeProducts);
  39:              }
  40:   
  41:              // Reality
  42:              decimal tax = 0.10m;
  43:              decimal price0 = _fakeProducts[0].Price + 
  44:                 (_fakeProducts[0].Price * tax);
  45:              decimal price2 = _fakeProducts[2].Price + 
  46:                 (_fakeProducts[2].Price * tax);
  47:              List<Product> results = Product.GetProducts(tax);
  48:              Assert.AreEqual(price0, results[0].Price);
  49:              Assert.AreEqual(price2, results[2].Price);
  50:          }
  51:   
  52:      }
  53:  }

In Listing 12, both the ConfigHelper and DataProvider classes are mocked within the expectation section. When the DataProvider.GetProducts() method is called, the static collection of products contained in the _fakeProducts variable are returned.

In the test section, the GetProducts() method is tested to determine if the correct tax calculation is performed on each product price. First, the correct value for the Price property, taking into account the tax, is calculated for two of the products. Next, the value of these Price properties are compared against the value of the Price properties returned by calling the GetProducts() method. If the property values match, then the test passes.

The important thing to understand about this test is that the configuration file is never touched and the database is never accessed. By taking advantage of the mock objects, all of the dependencies on external objects have been broken.

Summary

In this blog entry, I’ve provided you with a brief introduction to Mock Object Frameworks in general and Typemock Isolator in particular. The goal of this entry was to explain why you should care about Mock Object Frameworks.

In the first section, I explained the importance of Mock Object Frameworks in the context of Test-Driven Development and ASP.NET MVC. Next, I explained how you could perform basic mocking operations with Typemock Isolator. I provided code samples that illustrate how you can mock instantiating objects, making method calls, and passing method parameters. I also discussed mocking LINQ to SQL queries with Typemock Isolator. Finally, I tackled a more realistic sample of when you would need to mock objects when performing unit tests. You learned how to mock access to external objects such as a configuration file and the database.

5 Comments

  • Thanks Stephen, this is a great post about how to use TypeMock in TDD.

    I'm curious how it could be done to mock a nested method call. For example, if DoSomethingA() calls a prive method, DoSomethingB() internally, how can I isolate just the DoSomethingA() method so that I can test that method only?

  • Great question! I did some research, and it looks like the recommended way is to use the method described here:
    http://msdn.microsoft.com/en-us/library/ms184807.aspx

  • Thanks for the research. I've actually used that before in order to test private methods. My question was actually on how to test DoSomethingA(), without calling DoSomethingB(), which is nested within DoSomethingA().

    Any thoughts?

  • Eli, thanks. I see. You need to create a private accessor via VSTS or roll your own. Still pretty cool. It'd be nice to be able to do something like this.


    using ( .. )
    {
    recorder.MockNested(myClass.DoSomethingB());
    }
    myClass.DoSomethingA();

  • That would be nice!
    .NET doesn't allow accessing private methods that way, so if DoSomethingB is private we can't use such an API.

Comments have been disabled for this content.