'Expect' Consistency
For starters, NMock2 was more consistent in how the 'Expect' calls are made versus the way Rhino Mocks does it. In NMock2, the use of 'Expects' are the same whether you use a void method or a method that returns a value:
Expect.Once.On(mockFoo).Method("SomeMethodThatReturnsAValue")That is not the case with Rhino Mocks. 'Expects' can only be used with methods that return values and not with void methods.
Expect.Once.On(mockFoo).Method("SomeVoidMethod")
Recently, a new way of expressing 'Expects' with void methods was added to the Rhino Mocks framework but it relies on 'delegates'. Not sure if I really like the solution. It trades off one form of weak readability for another albeit different one.
This could be yet another reason to turn off newbies from testing with a mock framework such as Rhinos. It can be confusing. It is already quite a difficult endeavor to encourage software developers the virtues of unit testing. It is even more difficult to promote mock object testing so anything to lower the barriers is important and critical.
Understandable Exception Messages
In addition, Rhino Mock exception messages sometimes can be vague and unclear. This can be frustrating for new (and even existing) users.
For example, I was recently working on an old test fixture for a project which uses NMock2 and not Rhinos as its testing framework. To some degree, I felt a bit more productive with and in control of it because the error messaging is a lot more user friendly. I could more quickly determine the cause of a problem.
For example, below is an actual exception I received from NMock2:
NMock2.Internal.ExpectationException: not all expected invocations
were performed
Expected:
1 time: criteria.SetFirstResult(equal to <50>) [called 0 times]
1 time: criteria.SetMaxResults(equal to <5>) [called 0 times]
1 time: criteria.List(any arguments), will return
<System.Collections.Generic.List`1[System.DateTime]> [called 0 times]
Now, here is what I might get from Rhinos:
Rhino.Mocks.Exceptions.ExpectationViolationException:
ICriteria.SetFirstResult(50); Expected #1, Actual #0.
ICriteria.SetMaxResults(5); Expected #1, Actual #0.
Honestly, I like the first one better. It reads better to me. For one thing, Rhinos provides the raw (CLR?) object definition so that if the member is inherited from an interface or another class then it shows as it is defined for interface (i.e. "ICriteria") or the base class . Meanwhile, NMock2 shows the actual local variable name used in the code you are testing (i.e. "criteria"). Much faster to pinpoint the culprit.
In fact, where this really drives me crazy is for the domain objects (i.e. POCOs, business objects, etc.) for that same project. Every domain object inherits from IDomainObject so with Rhino Mocks I get this:
IDomainObject.DescriptionOK....but, which domain object is it? If I happen to have two or more domain objects being mocked/stubbed in my test it can get really hard figuring out the one it's complaining about. Instead, it would be nice if Rhino provided the following as does NMock2 using the variable name (assuming my domain object is named 'Foo'):
foo.DescriptionAnother example of the disparity between the two frameworks is if a property related exception occurs then the Rhino message would contain this:
IFooView.set_PageSizewhile NMock2 would provide this:
_view.PageSize \\ instance variable nameSome would say, "What's the big deal?", "Can't you figure out what it is?", "It only takes a few seconds to know what it is", etc. Well, that is the problem. If my brain has to stop to process what it is, even if it takes a few seconds, then that is slowing me down during my software development process. Multiply those "few" seconds by how many times you get Rhino exceptions like that and it does eat away at your development time. It does add up over time. It is not unlike trying to read code that is not very readable or well factored. Sure, you'll eventually figure out what it does but at the cost of precious dev time.
System.If you specify the wrong data type in the 'Return' method of an Expect (or LastCall) then the above exception will be thrown. For example, if the method or property is suppose to return a 'string' type value but you instead specify a 'DateTime' type as shown below:InvalidOperationException: Previous method 'IView.get_ ReturnSomeStringValue();' require a return value or an exception to throw.
DateTime date = DateTime.Todaythen you will receive the error message mentioned earlier.
Expect.Call(_view.ReturnSomeStringValue).Return(date);
// this will throw an exception
Specifying the proper type should fix the problem as follows:
string someStringValue = "some string value";The exception message should really be about checking for strong typing and not the absence or lack of a return value.
Expect.Call(_view.ReturnSomeStringValue).Return(someStringValue);
// this is ok