Rhino Mocks: Meaningful failure messages on expectations

Rhino Mocks: Meaningful failure messages on expectations

Recently, Ayende Rahien (Oren Eini) blogged about Planning for Rhino Mocks 4.0.  He’s calling for all of us to contribute our ideas on how to improve Rhino Mocks.  He set up a where we can submit our ideas and vote on them as well.

This lead me to review some of my more complex tests involving (I’m on version 3.6).  Were there mocking difficulties that I could propose a solution to?  Let’s find out…

A frequent pattern in my tests is verifying that my controllers pull properties from the views correctly.  The controller would construct a new entity object, providing the values from various view properties.  The entity object then would be passed to the data provider to save the entity.

The first attempt to test that a particular property was passed through correctly went something like this:

1: _model.AssertWasCalled( m => m.Add( null ),

2: o => o.Constraints(

3: Is.Matching( j => j.Account.Id == _accountId ) ) );

Fairly straight forward, but the failure message doesn’t provide much information:

1: failed: Rhino.Mocks.Exceptions.ExpectationViolationException: IMyModel.Add<Job>(Predicate (TestClass.<Test>b__12(obj);)); Expected #1, Actual #0.

Marginally better is the use of the Arg helper class:

1: var arg = Arg.Matches( j => j.Account.Id == _accountId );

2: _model.AssertWasCalled( m => m.Add( arg ) );

…with this failure message:

1: failed: Rhino.Mocks.Exceptions.ExpectationViolationException: IMyModel.Add<Job>(j => (j.Account.Id = (value(TestClass)._accountId ))); Expected #1, Actual #0.

This gave me a better idea as to the check that was failing (honestly that is what my test method name is for), but without helpful details.

So I changed the original approach slightly by adding an assertion within the constraint delegate, like so:

1: _model.AssertWasCalled( m => m.Add( null ),

2: o => o.Constraints(

3: Is.Matching( j =>

4: {

5: j.Account.Id.ShouldEqual( _accountId );

6: return true;

7: } ) ) );

Now I am using an explicit “ShouldEqual”, and I get:

1: failed:

2: Expected: 143

3: But was: 142

But, that “return true” is just ugly, and there’s a lot going on just to test that the account id was passed through correctly.

Then I discovered the Rhino Mock extension method GetArgumentsForCallsMadeOn:

1: var arg = (Job)(_model.GetArgumentsForCallsMadeOn( m => m.Add( null ) )[0][0]);

2: arg.Account.Id.ShouldEqual( _accountId );

After all, I didn’t really want to assert that the Add method was called.  I wanted to assert that the method was called with the correct parameters, but, with each parameter under a different test:

1: [Specification]

2: public void the_job_should_be_add_to_the_queue

3: {

4: _model.AssertWasCalled( m => m.Add( Arg.Is.Anything ) );

5: }

6: 

7: [Specification]

8: public void the_selected_account_id_should_be_used

9: {

10: var jobArg = (Job)(_model.GetArgumentsForCallsMadeOn( m => m.Add( null ) )[0][0]);

11: jobArg.Account.Id.ShouldEqual( _accountId );

12: }

13: 

14: [Specification]

15: public void the_selected_from_date_should_be_used

16: {

17: var jobArg = (Job)(_model.GetArgumentsForCallsMadeOn( m => m.Add( null ) )[0][0]);

18: jobArg.FromDate.ShouldEqual( _fromDate );

19: }

20: 

21: [Specification]

22: public void the_selected_to_date_should_be_used

23: {

24: var jobArg = (Job)(_model.GetArgumentsForCallsMadeOn( m => m.Add( null ) )[0][0]);

25: jobArg.ToDate.ShouldEqual( _toDate );

26: }

27: 

28: [Specification]

29: public void the_selected_line_items_should_be_used

30: {

31: var jobArg = (Job)(_model.GetArgumentsForCallsMadeOn( m => m.Add( null ) )[0][0]);

32: jobArg.LineItems.Select( li => li.LineItem.Id ).ShouldHaveSameElements( _lineItems );

33: }

That’s good enough for me.  I guess I will have to search for some other ideas to improve Rhino Mocks.

Tags:,

posted on Wednesday, September 9, 2009 1:08 PM

This article is part of the GWB Archives. Original Author: Will Smith

New on Geeks with Blogs

  • We Won The One Award I Actually Care About

    Full Scale made the Inc. 5000 for the fifth year straight, the 12th listing across my three companies. Here is why the one award you cannot buy is worth stopping for.

  • Your Customers Build the Features Now

    I let a tool I liked sit dead for a year rather than build the features I wanted. An MCP server meant I never had to, and your customers can do the same to your product.

  • Get the Size of a Directory in Linux the Easy Way

    du -sh for the quick answer, ncdu for the cleanup, df for the disk itself: every command for checking directory size in Linux, plus why du and df never agree.

  • Vim Search and Replace: The Ultimate Guide

    One :%s command replaces every match in a file before a find dialog would even open. The Vim substitute patterns worth the muscle memory: flags, ranges, capture groups, and multi-file edits.