Interested in a
Personal Demo ?


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

Testimonials


"I have been using Typemock Isolator for quite some time and I would like to thank the Typemock team for such a great product which I believe is the only product that makes Unit Testing really possible"
Rahul Das, Technical Lead, Bank Societe Generale
Success stories

PurchaseOptions


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

 

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.

 

 

Unit-Test Patterns For .NET - Part III Natural Mocks™

Abstract


In this series, we discussed unit-test patterns and the advantages that it brings. Although there is great power in using Typemock Isolator, there are times when the reflective API can falter when refactoring code. This article will look at how to test the interaction between classes using Natural Mocks™ to solve these problems. 

 

By Typemock
July 2007

 
 

Introduction

Many developers have seen the improvement in quality and increased speed that come from using Isolator. The framework reduces the amount of setup required for the tests and the need to change the production code just to make it testable. 

In the previous article we looked at the Typemock test patterns. We saw that this pattern can be used in most code scenarios. But the reflective way of defining the expectations has a few fallbacks, including:

  • Name typos are found at runtime
  • Refactoring the code leads to errors found during runtime
  • No code completion

We will go through the same examples as the previous article but using Natural Mocks™ instead.

Authenticating Again

Lets go back to our example of an Logger in our 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; they may even send an email to the system manager. The Logger already has many tests and we don't have to test these again.

Let's look again at our code:

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 also use a DataConnection class to setup our database.

So how do we test this? We already know that Isolator can help us test this in no time at all. But this time we will use Natural Mocks™.

Using Natural Mocks™

With Natural Mocks™ we can now record the expectations simply by calling the expected methods.

Here is how we ‘isolate’ the Logger and test our code:

[Test] public void Authenticated ()
{
      Authentication authenticator = new Authentication();
      // Start recording our expectations
    1 using (RecordExpectations recorder =RecorderManager.StartRecording())
      {
          // CAUTION: ALL calls here are mocked!!! 
    2     Logger.Log(Logger.NORMAL,"Entering Authentication");
          recorder.CheckArguments();

          Logger.Log(Logger.FAILED,"Login system failure");
         recorder.CheckArguments();

    3    DataConnection.Initialize();
         recorder.Throw(new DataConnectionException());
      }

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

 

Let's see what we did:

  1 Start recording our expectations.
  2 We wanted to see if  IsAuthenticated will write the correct logs to the Logger. We expected two calls. One with a normal message: Entering authentication; and another with a failure message: Login system failure. We now ask Typemock Isolatorto validate the parameters that will be passed to these call.
  3 We then mocked mocked DataConnection to simulate a system failure and throw an exception when the Initialize method is called.
  4 Finally, we verified that all  expected methods were called (this behaves the same as regular mock objects).

As in the previous example, we don't need to pass any objects to the test, this is done automatically by Isolator. This example also contains mocked static methods, although mocking instance methods is just as easy.

Using Chained Natural Mocks™

Typemock Isolator also enables to mock many methods in one statement. Let's look at an example. Suppose our code changed and looks as follows:

public bool IsAuthenticated(string name,string password)
{
   Logger.Log(Logger.NORMAL,"Entering Authentication");
   try 
   {
      // do something
      int Results = DataPool.GetDataBases().UserDataBase.
            PerformSelectPasswordCount(name,password);
      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
   }
}

 

Now, instead of mocking the DataPool and returning a mocked databases factory and returning a mocked UserDataBase only to mock the PerformSelectPasswordCount, we can do it in one line (see 3 below):

[Test] public void Authenticated ()
{
      Authentication authenticator = new Authentication();
      // Start recording our expectations
    1 using (RecordExpectations recorder =RecorderManager.StartRecording())
      {
         // CAUTION: ALL calls here are mocked!!! 
    2    Logger.Log(Logger.NORMAL,"");
         recorder.RepeatAlways();

    3    DataPool.GetDataBases().UserDataBase.
            PerformSelectPasswordCount("user","password");
         recorder.Return(0);
      }

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

 

Let's see what we did:

  1 We started recording our expectations.
  2 Then ignored all calls to Logger.Log.
  3 We simulated no records with user and password in the UserDataBase (return 0).
Here we used chained Natural Mocks™.
  4 We then verified that the expected methods were called (this behaves the same as regular mock objects).

Here is how we would do this without chained Natural Mocks™:

[Test] public void Authenticated ()
{
      Authentication authenticator = new Authentication();
      // Start recording our expectations
    1 using (RecordExpectations recorder =RecorderManager.StartRecording())
      {
         // CAUTION: ALL calls here are mocked!!! 
    2    Logger.Log(Logger.NORMAL,"");
         recorder.RepeatAlways();

    3   DataBases mockedDataBases = 
            (DataBases)RecorderManager.CreateMockedObject(typeof(DataBases));
         DataBase mockedUserDataBase = 
            (DataBase)RecorderManager.CreateMockedObject(typeof(DataBase));

    4    DataPool.GetDataBases();
         recorder.Return(mockedDataBases);

    5    mockedDataBases.UserDataBase;
         recorder.Return(mockedUserDataBase);

    6    mockedUserDataBase.PerformSelectPasswordCount("user","password");
         recorder.Return(0);
      }

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

 

Let's see what we did:

   1 Start recording our expectations.
   2 Ignore all calls to Logger.Log.
   3 Create mock objects needed for chained statements.
   4 Mock GetDataBases and return the mocked object.
   5 Mock UserDataBase and return a mocked DataBase.
   6 Mock our called method and simulate no records with user and
      password in the UserDataBase (return 0).
   7 Verify that all expected methods were called (this behaves the
      same as regular mock objects).
As we can see, this makes the tests setups more complex and harder to maintain.

 

Summary

In this article, we introduced the Natural Typemock test pattern. This pattern is a powerful way to isolate your code for unit tests and allow for better tests, less setup, and less tests coupling.

To download Typemock Isolator visit our Download page.

 Page 1 2 3 4 5