NMock (101)

Recently a new member of the development team questioned me on how various dependent objects, that are part of an implementation of an app we are building, could be tested using NUnit and NUnitASP, a set of unit testing tools. I stated that the simplest way I understand would be to "mock" the objects that the view (or the UI) and controller tiers under test are dependent upon. The tool that I am most familiar with is NMock. Since it had been a while since I had utilized NMock for testing purposes, I thought it would be good to review the documentation for the tool.

When you go to NMock’s web site, www.nmock.org, you see a section that provides an overview, a basic example of implementation, a list of constraints types for parameters that are passed in by the testing code, and a tips section. While this does provide good, basic information about the tool, there is nothing to substitute for good old hands-on testing to better understand how to implement a testing instrument like NMock.

First, I started with the NUnit tool and thinking about what is was I wanted test. The simplest thing I could think of was testing an object's method that returned a string that was passed to it via the method signature. What I created was a simple class conveniently named MockedObject, that implements the IMockedObject interface.

MockedObject.cs

    1 using System;

    2 

    3 namespace NMockTest

    4 {

    5     ///<summary>

    6     /// Summary description for IMockedObject.

    7     ///</summary>

    8     public class MockedObject : IMockedObject

    9     {

   10         #region IMockedObject Members

   11 

   12         public MockedObject()

   13         {}

   14 

   15         public string ReturnStringMethod(string input)

   16         {

   17             return input;

   18         }

   19 

   20         #endregion

   21     }

   22 }

IMockedObject.cs

    1 using System;

    2 

    3 namespace NMockTest

    4 {

    5     ///<summary>

    6     /// Summary description for IMockedObject.

    7     ///</summary>

    8     public interface IMockedObject

    9     {

   10         string ReturnStringMethod(string input);

   11     }

   12 }

Next, I setup a test fixture,NMockTestFixture.cs, in NUnit with the following test:

NMockTestFixture.cs

   25 [Test]

   26   public void TestMockedObject()

   27  {

   28       DynamicMock mockedObjectFromNMock = new DynamicMock(typeof(IMockedObject));

   29       ClassBeingTested testedClass = new ClassBeingTested((IMockedObject)mockedObjectFromNMock.MockInstance);

   30  }

Of course we get a compilation error since we do not have a ClassBeingTested object implemented. But this helps us think ahead to what we expect to do concerning the construction of the ClassBeingTested class. Next, we implement the ClassBeingTested class.

ClassBeingTested.cs

    1 using System;

    2 

    3 namespace NMockTest

    4 {

    5     ///<summary>

    6     /// Summary description for ClassBeingTested.

    7     ///</summary>

    8     public class ClassBeingTested

    9     {

   10         public IMockedObject _mockedObject;

   11 

   12         public ClassBeingTested(IMockedObject mockedOject)

   13         {

   14             this._mockedObject = mockedOject;

   15         }

   16 

   17     }

   18 }

Then, the lovely green bar:

Now time to add some new code to the test and make the test fail again. See line 31 in the test method below.

NMockTestFixture.cs

   25 [Test]

   26   public void TestMockedObject()

   27    {

   28        DynamicMock mockedObjectFromNMock = new DynamicMock(typeof(IMockedObject));

   29        ClassBeingTested testedClass = new ClassBeingTested((IMockedObject)mockedObjectFromNMock.MockInstance);

   30 

   31         mockedObjectFromNMock.Expect("ReturnStringMethod","Hello test");

   32    }

Sure enough, we have a failed test since we have not added implemented the ReturnStringMethod  method from the IMockedObject interface in the MockedObject class. This throws a good ole 'NMockTest.MockedObject' does not implement interface member 'NMockTest.IMockedObject.ReturnStringMethod(string)' error.

We then add the implementation of the ReturnStringMethod in the MockedObject class (lines 15 - 18)

MockedObject.cs

    1 using System;

    2 

    3 namespace NMockTest

    4 {

    5     ///<summary>

    6     /// Summary description for IMockedObject.

    7     ///</summary>

    8     public class MockedObject : IMockedObject

    9     {

   10         #region IMockedObject Members

   11 

   12         public MockedObject()

   13         {}

   14 

   15        public string ReturnStringMethod(string input)

   16        {

   17             return input;

   18        }

   19 

   20         #endregion

   21     }

   22 }

And wah lah! Another green bar.

Now to add the final lines to the test code. Let's utilize the mocked object. To do this we invoke a method of the class that we are testing. We will call this GetString and pass in the string of "Hello Test".

NMockTestFixture.cs

   25 [Test]

   26    public void TestMockedObject()

   27   {

   28       DynamicMock mockedObjectFromNMock = new DynamicMock(typeof(IMockedObject));

   29       ClassBeingTested testedClass = new ClassBeingTested((IMockedObject)mockedObjectFromNMock.MockInstance);

   30 

   31        mockedObjectFromNMock.Expect("ReturnStringMethod","Hello test");

   32        testedClass.GetString("Hello test");

   33   }

Ooops. When we run it, guess what? You got it, we did not have the GetString method implemented in the ClassBeingTested object and it threw an exception. Here is the ClassBeingTested class with the added method: 

ClassBeingTested.cs

    1 using System;

    2 

    3 namespace NMockTest

    4 {

    5     ///<summary>

    6     /// Summary description for ClassBeingTested.

    7     ///</summary>

    8     public class ClassBeingTested

    9     {

   10         public IMockedObject _mockedObject;

   11 

   12         public ClassBeingTested(IMockedObject mockedOject)

   13         {

   14             this._mockedObject = mockedOject;

   15         }

   16 

   17         public string  GetString(string myInput)

   18         {

   19            return _mockedObject.ReturnStringMethod(myInput);

   20         }

   21     }

   22 }

After, implementing the GetString method, the tests pass.

In conclusion, our (or at least my) understanding is more improved by a hands-on implementation of what we read in the NMock documentation. One thing of note with the current NMock documentation, (as of 4-4-2005) on the website is the document demos a ExpectNotCalled(“MethodName”) method. This method, that takes the method name of the mocked object, is used to assert that the method name passed in is not invoked. Yet, when viewing the assembly in the .Net ildasm tool (The MSIL Disassembler that is part of the .Net Framework Tools), there is no ExpectNotCalled method. There is however a ExpectNoCall() method that fulfiles the same function.This is cleary a typo on the site.

I know that I have no room to talk but wanted to point out how that merely reading content, without unit testing, means very little concerning having a more exact understanding. In any event, this shows that approaching a new situation, or what those in the agile camp have called a learning "spike," with a hands-on, TDD mentality results in a clearer. less problematic learning experience.