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 | Main | Mocking and Isolation in Unit Testing »

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.