Introductin Rhino.Mocks

time to read 17 min | 3292 words

 Okay, so I've been so deeply coding there that I didn't get time to go online and post anything, but now I've nearly finished it, I'm ready for a 0.9 release.

The To Do List:

  • Add a way to easily ignore a method (currently can only do so globally).
  • Dog food it like crazy, the tests pass, but I want more experiance using it first. I made some UI (interface speaking) that I'm not so sure of.
  • Go over all possible errors and make sure that they don't retain the old interface.

The good:

  • Constraints: I added NMock's like Constraints (currently just a subset). Work just like NMock, only you get strongly typed interfaces and refactoring support from all the tools.
  • Callbacks: I added a native [Read: Not a hack] way to get called when a mocked method is called.
    Detailed Error Messages: Very good error messages support. This is the work of EasyMocks.Net, actually. But I added some minor stuff as well.
  • .Net Interface: No longer burdened by its Java heritage, Rhino.Mocks has a shiny new dotNetty like interface.
  • No more fake constraints just to get NMock to decide which overload I wants. (See the second example, for the AddException() call)

The code:
Here is one of NQA's tests [one of the simplest ones] using NMock:

[Test]

public void SaveProjectAs_CanBeCanceled()

{

      mockProjectView.ExpectAndReturn("Title",prj.Name);

      mockProjectView.ExpectAndReturn("Ask",null,new IsTypeOf(typeof(string)),null);

      Assert.IsFalse(presenter.SaveProjectAs());                 

}

Here it is using Rhino.Mocks:

[Test]

public void SaveProjectAs_CanBeCanceled()

{

      getDummy = projectView.Title;

            mockProjectView.Returns = prj.Name;

      projectView.Ask("Project name:","Test Proejct")

            mockProjectView.Returns = null;

      Replay();

      Assert.IsFalse(presenter.SaveProjectAs());           

}

As you can see, the Rhino.Mocks one is longer, because we need to specify the returns seperatedly. But the advantage is in code that is both more explicit and clearer.

Here is one of the more complex tests in NQA, it verify that a thread properly handle its exceptions. This was very difficult test to write because I need some behaviour in the mocked class. The options were delegating constraits or write a mock by hand. The hack was faster. The test include that incredibly hackish DelegatingConstraintWithArgs as well, which is another red flag.

[Test]

public void ExceptionInQuery_DoesNotThrowButReportsException()

{

      DelegatingConstraintWithArgs executeInUI = new DelegatingConstraintWithArgs(

            new ExecuteInUIThreadDeleagate(ExecuteInUIThread),2,"");   

      DelegatingConstraintWithArgs executeCommand = new DelegatingConstraintWithArgs(

            new ExecuteCommand(executeCommandDelegate),1,"");                      

      mockMainPresenter.Expect("EnqueueCommand",executeCommand);

      mockIQueryView.Expect("ExecuteInUIThread",executeInUI,executeInUI);

      mockIQueryView.SetupResult("HqlQueryText","Bad HQL Query");

      mockIQueryView.SetupResult("Parameters",new Hashtable());

      mockIQueryView.Expect("StartWait",null,null,null);

      mockIQueryView.Expect("EndWait",new IsTypeOf(typeof(string)));

      mockIQueryView.Expect("AddException",new IsAnything());

      presenter.ExecuteQuery();

      executioner.Run();

      while(!executeInUI.DelegateCalled)

            Thread.Sleep(100);

}

I'm refactorin to Rhino.Mocks as I'm writing this... please hold:

[Test]

public void ExceptionInQuery_DoesNotThrowButReportsException()

{

      executeInUI_DelegateCalled = false;

      mainPresenter.EnqueueCommand(null);

            mockMainPresenter.Callback = new ExecuteCommand(executeCommandDelegate);

      queryView.ExecuteInUIThread(null,null);

            mockIQueryView.Callback = new ExecuteInUIThreadDeleagate(ExecuteInUIThread);

      getDummy = queryView.HqlQueryText;

            mockIQueryView.AlwaysReturn = "Bad HQL Query";

      getDummy = queryView.Parameters;

            mockIQueryView.AlwaysReturn = new Hashtable();

      queryView.StartWait("Executing query",100,1000);

      queryView.EndWait("An exception occured executing query");

      queryView.AddException(null);

            mockIQueryView.Constraints(new IsTypeOf(typeof (QueryException)));

      Replay();

      presenter.ExecuteQuery();

      executioner.Run();

      while(!executeInUI_DelegateCalled)

            Thread.Sleep(100);

}

Both tests are without their setup/teardown & supporting methods, but they should provide a good indication to the way that writing tests in both libraries encourage. In the Rhino.Mocks test I used all three approachs for verifying. Straight equility, when I'm simply calling the method and passing the parameters that should be passed during the test. Constraints validation. And custom validation & actions via callbacks.

This makes Rhino.Mocks a hybrid, with the good of both worlds. I'll finish porting NQA to Rhino.Mocks and then I'll post some more about it, a detailed fact sheet (NMock's documentation is the best, accurate, to the point, and all of it in one page) and, of course, the code.