Saturday, July 23, 2011

RhinoMocks constraint LINQ gotcha

At work the other day I was trying to write some unit tests for a method that used some LINQ statements on a list that I was mocking using RhinoMocks. I put a constraint that a method was called that had a certain count of objects being passed into it via generic IEnumeralbe list. To my suprise, it kept failing saying the constraint had not been made. First thing I did was fire up the debugger and take a look at the list of objects I was passing to the method, and sure enough it was the number I was looking for. I began to start poking around and realized I was passing a list that I had done a LINQ where statement and I never called any method like 'ToList()' or 'ToArray()' to execute the chain of commands I had applied with LINQ. As soon as I called one of those methods the constraint was correct! It seems to be a bug in RhinoMocks that is not actually calling any methods that stop the delayed execution and really make the statement be applied.

Here is a very quick sample that I threw together to show the problem. I used NuGet to get the version of RhinoMocks I used, so I haven't tested this issue with the latest and greatest version per say.



   1:  public class FakeService

   2:      {

   3:          private readonly INumberHelper _numHelper;

   4:          

   5:          public FakeService(INumberHelper numHelper)

   6:          {

   7:              _numHelper = numHelper;

   8:          }

   9:          

  10:          public void PrintAllEventNumbers_NotWorking()

  11:          {

  12:              var evenNumbers = _numHelper.GetAllNumbers()

  13:                  .Where(x => x%2 == 0);

  14:              

  15:              _numHelper.PrintNumbers(evenNumbers);

  16:          }

  17:   

  18:          public void PrintAllEventNumbers_Working()

  19:          {

  20:              var evenNumbers = _numHelper.GetAllNumbers()

  21:                  .Where(x => x % 2 == 0);

  22:   

  23:              _numHelper.PrintNumbers(evenNumbers.ToArray());

  24:          }

  25:      }

  26:   

  27:      public interface INumberHelper

  28:      {

  29:          IEnumerable<int> GetAllNumbers();

  30:          void PrintNumbers(IEnumerable<int> numbersToPrint);

  31:      }



And this is the unit tests to show how it works and doesn't work.



   1:  [Test]

   2:          public void TestDoesNotWork()

   3:          {

   4:              var numHelper = MockRepository.GenerateMock<INumberHelper>();

   5:              var fakeService = new FakeService(numHelper);

   6:   

   7:              numHelper.Stub(x => x.GetAllNumbers())

   8:                  .Return(Enumerable.Range(0, 100));

   9:   

  10:              fakeService.PrintAllEventNumbers_NotWorking();

  11:   

  12:              numHelper.AssertWasCalled(x => x.PrintNumbers(Arg<IEnumerable<int>>.List.Count(Rhino.Mocks.Constraints.Is.Equal(50))));

  13:          }

  14:   

  15:          [Test]

  16:          public void TestDoesWork()

  17:          {

  18:              var numHelper = MockRepository.GenerateMock<INumberHelper>();

  19:              var fakeService = new FakeService(numHelper);

  20:   

  21:              numHelper.Stub(x => x.GetAllNumbers())

  22:                  .Return(Enumerable.Range(0, 100));

  23:   

  24:              fakeService.PrintAllEventNumbers_Working();

  25:   

  26:              numHelper.AssertWasCalled(x => x.PrintNumbers(Arg<IEnumerable<int>>.List.Count(Rhino.Mocks.Constraints.Is.Equal(50))));

  27:          }