NMock Out Parameters

I’ve often thought that NMock is under-documented. The way I’ve started to learn how to use this extremely useful library is through the well-formatted examples that Ian has left around. Practical examples often show you far more than a glossary of terms or a list of available methods.

This week Andy and Drew were wanting to test a method which had both a return value and an out parameter. A quick look through the NMock Quickstart, Tutorial, Advanced or Cheat Sheet pages didn’t show any way of achieving this. Thankfully the Blogosphere came through once again to prove that you can do this with NMock.

Say you have a method in your mockable class called:

public string AddGreeting(string userName)

you could expect to write a Stub declaration such as

Stub.On(mockObject)
.Method(AddGreeting)
.With("World")
.Will(Return.Value("Hello World"));

If your method has an out parameter as well, such as:

public string AddGreeting(string userName, out int characterCount)

then your Stub declaration will need to look like this:

string expectedUserName = "World";
Stub.On(mockObject)
.Method(AddGreeting)
.With(Is.EqualTo(expectedUserName), Is.Out)
.Will(new SetNamedParameterAction(["characterCount", 11), Return.Value("Hello World"));

The Is.Out is setting the expectation that one of the parameters you’re specifying is an Out parameter. The SetNamedParameterAction object is undocumented and has been found using reflection. This is a useful feature which hasn’t been documented. The NMock project appears to being built using continuous integration these days but that obviously doesn’t happen with the project documentation.
[Edited to update code sample to make use of Is.EqualTo() and explicitly declared variables for any non-out parameters in the method call. 18th Feb 2009]

9 thoughts on “NMock Out Parameters”

  1. Yours is the only code that gives me an example of setting up an out parameter but I cannot get it to work.

    Could you tell me what is wrong with how I have set up the following. I basically want to test that the out string get a value of 31NAA6602100000.

    ICoordinateConverter instance = mocks.NewMock();
    Expect.Once.On(instance).Method(“ConvertDDToMGRS”).With(0, 0, SpheroidDatum.SPHEROID_WGS84, MGRSPrecision.MGRSPRECISION_1METRE, Is.Out).Will(new SetNamedParameterAction(“mgrsString”, “31NAA6602100000”));
    instance.ConvertDDToMGRS(0D, 0D, SpheroidDatum.SPHEROID_WGS84, MGRSPrecision.MGRSPRECISION_1METRE, out mgrsString);

    Cheers

    Joseph

    1. I created an example to see what might be going wrong. I’d suggest you create your new mock with:
      ICoordinateConverter instance = mocks.NewMock();
      as you’ve got a specific interface you want the mock object to.

      The reason it could be failing is the other parameters in the With statement may need to be changed from literals to use the Is.EqualTo() function. Give it a go with Is.EqualTo() for any static parameters like your 0,0, SpheroidDatum.SPHEROID_WGS84 etc by assigning these to variables first, and then using them like I did in the following example:

      using System;
      using NMock2;
      using NMock2.Actions;

      namespace NMockTest
      {
      public interface ITest
      {
      void TestMethod(int first, out int second);
      }

      class Program
      {
      static void Main(string[] args)
      {
      // Create the Mockery and set up the Mock Object
      Mockery mocks = new Mockery();
      ITest testInstance = mocks.NewMock();

      // Set up the expectations
      int firstParameter = 0;
      Stub.On(testInstance).Method(“TestMethod”).With(Is.EqualTo(firstParameter), Is.Out).Will(new SetNamedParameterAction(“second”, 2));

      // Test the method
      int outTest;
      testInstance.TestMethod(0, out outTest);

      // Output the result
      Console.WriteLine(outTest);
      Console.ReadLine();
      }
      }
      }

      In the above example, if I simply did the following:
      Stub.On(testInstance).Method(“TestMethod”).With(0, Is.Out).Will(new SetNamedParameterAction(“second”, 2));
      you get the following exception:
      “unexpected invocation of test.TestMethod(, out)”

      Let me know if it works for you,
      Cheers!

  2. Thanks, I went back to basics and came up with the following which I can build on
    namespace MockTests.OutTests
    {
    public interface IMockOut
    {
    void TestMethod(int first, out int second);
    }

    public class MockOut : IMockOut
    {
    #region IMockOut Members

    public void TestMethod(int first, out int second)
    {
    second = first * 2;
    }
    #endregion
    }

    namespace MockTests.OutTests.Tests
    {
    [TestFixture]
    public class MockOutTests
    {
    private Mockery mocks;
    private IMockOut mockOut;

    [SetUp]
    public void SetUp()
    {
    mocks = new Mockery();
    mockOut = mocks.NewMock();
    }

    [Test]
    public void SimpleMockOutTest()
    {
    int firstParameter = 1;
    Stub.On(mockOut).Method(“TestMethod”).With(Is.EqualTo(firstParameter), Is.Out).Will(new SetNamedParameterAction(“second”, 2));

    // Test the method
    int outTest;
    mockOut.TestMethod(1, out outTest);

    Assert.AreEqual(2, outTest);
    }
    }
    }
    cheers

  3. I’m stuck. Trying to build with this syntax and I get:

    “The type or namespace name ‘SetNamedParameterAction’ could not be found (are you missing a using directive or an assembly reference?)”

    using NMock2;
    referencing NMock2.dll built 30-Jan-2008

    ???????

    Any guidance would be much appreciated!

    Thanks,
    Bob

  4. Bob,

    SetNamedParameterAction is located under NMock2.Actions. You can either use ‘new NMock2.Actions.SetNamedParameterAction(…)’ in your code or add a ‘using NMock2.Actions;’ under ‘using NMock2;’.

    –Mike

  5. To do this in VB.NET remember that Is is a keyword so you would need to replace Is.Out with NMock2.Is.Out.

    (Please, no VB.NET comments – the language is my client’s choice and I am just pointing this out in case anyone else has to use VB.NET when trying this)

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.