www.codinghillbilly.com   kyle.baley.org  Subscribe / Contact
 
 
 
 
LATEST POSTS
Saturday, April 28, 2007

There's a good chance I'll lose some credibility with this post and that many people will file it under "stating the obvious". But a wise man once told me, "There aren't any stupid questions, are there?" so I'll proceed assuming someone has had the same trouble I did when I was learning mock objects, and more specifically, Rhino Mocks (or RhinoMocks or Rhino.Mocks depending on who you talk to).

My main conceptual stumbling block was in the use of the term "expectation". To explain, here is the introductory example from the Rhino Mocks documentation:

1  [Test]
2  public void SaveProjectAs_CanBeCanceled()
3  {
4    MockRepository mocks = new MocksRepository();
5    IProjectView projectView = (IProjectView)mocks.CreateMock(typeof(IProjectView));
6    Project prj = new Project("Example Project");
7    IProjectPresenter presenter = new ProjectPresenter(prj,projectView);
8    Expect.Call(projectView.Title).Return(prj.Name);
9    Expect.Call(projectView.Ask(question,answer)).Return(null);
10   mocks.ReplayAll();
11   Assert.IsFalse(presenter.SaveProjectAs());          
12   mocks.VerifyAll();
13 }

(Note: The example is a little dated due to Rhino Mocks' new CreateMock<T> but that wasn't the confusing part. Nor was the typo in line 4. Read on, reader, read on.)

I read/scanned the surrounding text several times trying to determine where the magic was happening. Focussing on line 8, here was my interpretation:

  • At some point during this test, there will be a call to projectView.Title. When that happens, I expect it's value to be identical to prj.Name. If it isn't, the test fails.

That is, I thought the expectation itself was doing the testing. Ya, ya, stop laughing. Sue me for assuming the English definition of "expect". (OK, so the expectation is on the .Call, not the .Return; I'm using Reverse Polish Notation, SOP in the Coding Hillbilly handbook.)

What line 8 *really* says is (to the best of my knowledge at least):

  • At some point during the test, I expect a call will be made to projectView.Title. When that happens, return prj.Name as its value. If the call isn't made, the test fails.

The good part is, realizing this made ReplayAll and VerifyAll that much less confusing particularly around the Replay State that the documentation assumes I know anything about.

It took a while for me to understand the Replay State. As far as I can tell, MockRepository.ReplayAll turns on a switch. When that switch is on, all calls to methods/properties on the mock objects are recorded and compared to the expectations (set before ReplayAll is called). Then when VerifyAll is called, the switch is turned off and we check to see if all expectations have been met.

Put another way:

  • Create your mock objects
  • Tell the mock framework which properties/methods you expect will be called. Along with this, you can specify which values you want to return when those methods/properties are called.
  • Start recording (see line 10). For all code executed after this, start comparing it with our expectations.
  • Execute code to be tested
  • Stop recording (see line 12). See which expectations have not been met and fail the test if any exist.

So again, the test will fail if you set an expectation that a method will be called and it isn't. I believe the reverse is true as well. That is, if a method is called and you *haven't* set an expectation on it, the test will fail also. But don't quote me on that.

I'm sure all of this is being explained much more clearly as I type this at the Calgary Code Camp at either James Kovacs' or Terry Thibodeau's sessions but I'm posting it anyway to justify the time spent ignoring my daughter.

Tuesday, May 01, 2007 8:21:59 AM (Eastern Standard Time, UTC-05:00)
Your newly gained understanding is correct, though your "It took a while for me to understand the Replay State." paragraph is a bit misleading. When you first create a MockRepository, all mock objects are in "record mode". They are listening for calls to tell them what to do. When you say mocks.ReplayAll(), you are putting the MockRepository in "replay mode" where mock objects return the values set by the expectations in "record mode". When you say mocks.VerifyAll(), the mock objects are checked to see if the expected method calls have been made. RhinoMocks would probably be easier for newcomers to grok if RhinoMocks had a mocks.Record() method that you called after creating the MockRepository. You are correct in your second to last paragraph. VerifyAll() fails if you call methods that haven't had expectations set, don't call methods that have had expectations set, or call methods more times than you said they should be. To elaborate on the last point, you can say Expect.Call(projectView.Title).Repeat.Twice.Return(prj.Name) to expect two calls. There are other repeats to say any calls, at least one, between 5 and 13, etc. If you find yourself using a lot of .Repeat.Any on a mock, you should check out mocks.DynamicMock(), which creates a mock object that doesn't care whether it's methods are called. You have to set up expectations (so RhinoMocks knows what to return), but not calling those methods does not cause failure. Oren's post on interaction-based testing is well worth a read as to when you should use the two types of mock objects: http://www.ayende.com/Blog/archive/2007/04/05/Guidelines-to-using-Interaction-Based-Testing.aspx
Comments are closed.

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

Copyright © 2010 Kyle Baley. All rights reserved.
 
CATEGORIES
.NET General (18) alt.net (4) altnetconf (9) ASP.NET AJAX (40) ASP.NET MVC (29) Bahamas (1) Bahanet (9) BDD (1) Brownfield (21) Career (10) Castle (1) Code coverage (1) Code review (2) Coding Style (6) Communication (1) Community (18) Conscientious Coding (35) Continuous Integration (11) dasBlog (12) Development (16) DevTeach (4) Domain (2) Environment (4) Estimating (1) Featured (14) Flamingo (10) Games (1) Google App Engine (3) GWT (9) Hardware (6) Java (2) Javascript (7) Linq (2) Livelink (6) Lucene.NET (2) MbUnit (1) Metrics (2) Miscellaneous (25) Mocking (4) NAnt (4) NHibernate (12) NInject (1) Office (3) Office Development (6) Open Rasta (1) Patterns (6) Presenting (14) Professional Development (15) Refactoring (10) ReSharper (11) REST (3) S#arp Architecture (5) Security (3) Software (11) Sundry (19) TDD (19) Tools (22) User Interface (6) Utilities (9) Visual Studio (8) VSTO (1) Web development (12) Windows (3) Working Remotely (17) Workplace (3) Writing (6)
 
LATEST POSTS
 
POPULAR POSTS
 
 
ARCHIVE