September 2009 Entries

Okay, so recently I was working on a new suggestion for my client.  It was a fairly simple request; create a snapshot of data to be used in monthly calculations, with the option to regenerate the snapshot at a later time.  The snapshot was easy.  I thought regenerating the snapshot would be easy too, but somehow I found a way to make it hard.

The first mistake I made was not writing a test first.  Really this had a lot to do with laziness.  The data that I am capturing is only available via a view into a proprietary database.  Somehow, I needed to figure out how to modify the data behind the view.  It seemed really hard, so I skipped it.

That decision came back to bite me.  I submitted the changes to the customer and soon they reported that they weren’t working.  Perhaps there was a little more than laziness here (perhaps arrogance).  I manually tested my changes, but I didn’t cover every aspect.  As it turns out, the snapshot was being updated correctly, but, the monthly calculations weren’t getting updated.

So now that I got a feature returned as a failure, I decided I better write that test.  In fact, following TDD principles, I knew I should write a test that would fail due to the reported defect.  I started on the test and hit a roadblock and was about to give up on it again.  I mean, this testing stuff is hard!

I chatted with a coworker asking his advice on how to test the data change behind the view.  He provided a simple and elegant solution.  We set up a test data script that would create a “test” table that duplicates the structure of the view.  Then we simply redirected the synonym from the view to the test table.  Now we essentially have a fake view.  And since it is a table, we can manipulate the data to our hearts content.

From this point I was able to continue writing my test.  Everything looked good.  I was sure to test all of the aspects of the new requirements that I could think of.  When I was done, I ran the tests and verified the defect.

Finally, I could fix the problem.  I figured out the problem and was able to fix it fairly easily.  Well, not so fast.  My test was still failing!  I spent hours on the issue.  I knew that the fix was correct.  I tried all kinds of debugging attempts.  I even changed to code to force it to be wrong for a different reason.  Everything seemed to be fine, except my test was still failing.  I was beginning to think that there was a bug in the test itself.

As it turns out, there was more than one problem with the original code.  The second issue was that I had an update statement that performed a join to the snapshot data, but was missing a critical condition in the join.  To uniquely identify the snapshot data, I needed to join to two fields and I forgot one.  So, the update was actually executing multiple times and the last time wasn’t the one I was expecting.

Had I persisted in my laziness and simply added the fix and sent it to the customer, I’d be embarrassed yet again.  But this time, the test saved me.  How many times do I have to learn this lesson?

Tags: ,

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 forum where we can submit our ideas and vote on them as well.

This lead me to review some of my more complex tests involving Rhino Mocks (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<Job>( null ),
   2:                         o => o.Constraints(
   3:                                  Is.Matching<Job>( 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<Job>.Matches( j => j.Account.Id == _accountId );
   2: _model.AssertWasCalled( m => m.Add<Job>( 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<Job>( null ),
   2:                         o => o.Constraints(
   3:                                  Is.Matching<Job>( 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<Job>( 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<Job>( Arg<Job>.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<Job>( 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<Job>( 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<Job>( 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<Job>( 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.