Interested in a
Personal Demo ?


Name* :
Please Enter your Name
Company E–Mail* :
Please Enter a Valid Email

Testimonials


"I thank the heavens for Typemock. This is E.X.A.C.T.L.Y what I have been looking for. Thank you so much guys."
Michael Nyika, Fool.com
Success stories

PurchaseOptions


US Toll Free
866–895–4680
Outside US
+44–203–150–0293
Get your printable quote
Buy online

 « Webinar: How to implement unit testing in your company and teams | Main | Unit-Test Patterns for .NET - Part III Natural Mocks™ »

Unit Test Patterns For .NET - Part II - The Typemock Isolator Pattern

Abstract


Programmers who have incorporated unit testing into their development process already know its advantages: cleaner code, courage to refactor, and higher speed. But even the most die-hard unit testers can falter when faced with testing a class that relies on system state for its behavior. This article looks at the Typemock Isolator pattern that can help you solve these problems. 

 

 
 

Introduction

Many developers have seen the improvement in quality and increase in speed that come from having a comprehensive unit-test suite. But writing good unit tests takes time and effort. Because each unit cooperates with others, writing a unit test can involve a significant amount of setup code. This makes tests more expensive, and in certain cases they can be almost impossible to implement, especially when methods depend on other hard-to-control things such as the network, a database, or even ambient temperature.

In our previous article we looked at some unit-test patterns and saw that there are many cases in which unit-test patterns are not enough. As a result, developers have to refactor their code to make it 'testable'. These cases include:

  • Singleton classes
  • Calls to static members
  • Objects that do not have an interface (as required for mock object pattern)
  • Objects that are instantiated in the code being tested
  • Objects that are not passes to the method (as required for mock object pattern)

Authenticating Again

Lets go back to our example of an Authentication class, as seen in the first part of this series. We will see some code that is impossible to test without modifications.

We have decided to use a Logger in the code. The Logger is responsible for logging the authentication process. This class reads an external configuration (perhaps from the database) and decides what severity to log and where to log. Logs of different severities can be logged in different places and could even send an email to the system manager. The Logger  already has vast tests and we don't need to test these again.

Let's look at the code again:

public bool IsAuthenticated(string name,string password)
{
      Logger.Log(Logger.NORMAL,"Entering Authentication");
      try 
      {
            DataConnection.Initialize();
            // do something
            int Results = DoSelect(statement);
            bool isOk = Results==1;
            if (isOk) 
            {
                  Logger.Log(Logger.NORMAL,"User Authenticated "+name);
            } 
            else 
            {
                  Logger.Log(Logger.SECURITY,"User login failed "+name);
            }
            return isOk;
      } 
      catch (Exception ex) 
      {
            Logger.Log(Logger.FAILED,"Login system failure",ex);
            // handle exception
      }
}

 

We are also using a DataConnection class to setup our database.

So how do we test this? We want to test how our code reacts to a system failure (for example, the user database is corrupt). We can do one of the following:

  1. Set up the logging system with all its subsystems (for example, the email subsystem) and check that the logs have been sent. This can lead to substantial setup and a greater coupling of the tests. A class may change some APIs and the test would fail even though the unit actually works.
  2. Write some code in the logging system to flag the system that we are now in testing mode. For example,  Logger.SetTestingMode=true. This will lead to having debug code in our production code, or having a different code for testing and production, in which case we would not be really testing our production code
  3. We could create an ILogger interface and let the Logger implement it and use a mock object. This will lead to passing the ILogger through the code, resulting in more coupling between the objects. This is a bad object-oriented procedure. In this case, this method is not possible as the Logger.Log method is static.

And we have to do make these changes in the DataConnection class as well!

Using Typemock Isolator

There is another solution. Using aspect-oriented development technology we can 'intercept' the calls to  Logger and mock these up. This is called Typemock, also known as, Virtual Mocks.

A mocked type relates to mocked objects like types relate to objects. Simply put, a mocked type will mock the methods of all objects as if we are using a mocked object. 

Typemock Isolator provides a typemock framework. Here is how we ‘isolate’ the Loggerand test our code:

[Test] public void Authenticated ()
{
      Authentication authenticator = new Authentication();
      // Initialize Type Mocks (This can be done in the [setup])
      MockManager.Init ();
      // the Logger class is now being mocked
    1 Mock logMock = MockManager.Mock(typeof(Logger)); 
      // set up our expectations 
    2 logMock.ExpectCall("Log").Args(Logger.NORMAL,"Entering Authentication");
      logMock.ExpectCall("Log").Args(Logger.FAILED,"Login system failure");

      // the DataConnection class is now being mocked
    3 Mock dbmock = MockManager.Mock typeof(DataConnection)); 
      // set up our expectations - make a system error 
    4 mock.ExpectAndThrow("Initialize",new DataConnectionException());

      // authentication should fail
      Assert.IsFalse(authenticator.IsAuthenticated("user","password"));
      // Verify that all calls were made
    5 MockManager.Verify();
}

 

Let's see what we did:

  1 Set up the mock of Logger - all calls to logger will be intercepted.
  2 Check if  IsAuthenticated writes the correct logs to the Logger. We expect two calls. One with a normal message: Entering authentication, and another with a failure message: Login system failure. It is at this point that we tell Typemock Isolatorto validate the parameters that will be passed to these call.
  3 Mock the DataConnection to simulate a system failure.
  4 Throw an exception when the Initialize method is called.
  5 Verify that all expected methods were called (this behaves the same as regular mock objects).

The attentive reader may have noticed that we don't pass any mocked object to the code we are testing (actually, the only time we call our code is in the Assert statement). This is actually the power of Typemock objects. This code is much more elegant than the other solutions, and we can keep to the guidelines of writing good tests:

  • Test isolated code.
    We isolate our code completely by returning mocked values and checking the 'messages' between objects.
  • Tests should be fast.
    We don't need to set up coupled code, which makes the test faster.
  • Tests have to be self contained.
    The entire scenario is contained in the test (there is no need to connect to a corrupt database).
  • Tests have to be run by multiple users.
    As there are no shared resources, this issue is fine.
  • Tests should be independent.
    This test can run alone.
  • Maintainable.
    As the test is simple, it is easier to maintain. Even if the logger changes its internal configuration, the test will not be affected.

How does it work?

Typemock Isolator uses an aspect-oriented programming design that creates a mock aspect. Internally, it uses the .NET Framework profiler API to monitor an application's execution. When a method is loaded by the CLR, Typemock Isolator retrieves the IL and replaces it with instrumented IL code. Typemock Isolator does not change your original IL code, it simply inserts new code that calls the Typemock Isolator framework and returns mocked values.

The Typemock Isolator framework is quite extensive and has the following features.

  • Mock inserter AOP engine
  • Can mock all concrete types
  • Allows arbitrary calls, or rejects any method calls that weren't explicitly expected
  • Specifies sequenced return values or exceptions
  • Specifies default return values or exceptions
  • Supports properties and indexes
  • Specifies dynamic return values based on parameters
  • Checks argument lists
  • Specifies custom checking of arguments

Summary

In this article, we introduced the Typemock Isolator test pattern. This pattern is a powerful method to isolate your code for unit tests and allows for better tests, less setups, and less tests coupling.

Go to: Unit Test Patterns - Part III - Natural Type Mocks

To download Typemock Isolator visit our Download page.