Tutorial :IEnumerable evaluation and unit testing



Question:

After reading another SO thread (Should parameters/returns of collections be IEnumerable<T> or T[]?), I am of the opinion that a unit testing 'issue' I'm having is probably related to what the thread refers to as "late evaluation".

I am passing three different but related flavors IEnumerables from a presenter class to my UI. The best news is that it works, but I can't seem to find a way to make a unit test that definitively proves that the right IEnumerable is being passed at the right time.

I have a facade with the following method:

public IEnumerable<DynamicDisplayDto> NonProjectDtos  {      get      {          var activities = LeaveTimeActivities.Cast<TimeSheetActivityBase>()              .Concat(AdministrativeActivities.Cast<TimeSheetActivityBase>());          return _assembler.ToActivityDtoCollection(activities, _timeSheet);      }  }  

In a presenter, I load a widget:

private ITimeSheetMatrixWidget _nonProjectActivityMatrix;  public ITimeSheetMatrixWidget NonProjectActivityMatrix {      set {          ..           // load the activities          _nonProjectActivityMatrix.Load(Facade.NonProjectDtos);          ...      }  }  

Then the test on a mocked matrix (using Rhino Mocks):

[Test]  public void SettingTheWidget_TriggersLoad_NonProjectActivities() {      var f = _getFacade();      ...      // inject the mocked widget & trigger the Load      var widget = MockRepository.GenerateMock<ITimeSheetMatrixWidget>();      timeSheetPresenter.ActivityMatrix = widget;        widget.AssertWasCalled(x => x.Load(f.NonProjectDtos),           mo =>mo.IgnoreArguments()); <-- only way not to fail      //widget.AssertWasCalled(x => x.Load(f.NonProjectDtos)); <-- error  }  

If I look in the debugger, I can see that the IEnumerable load arg is evaluating to Domain.TransferObjects.TimeSheetDtoAssembler +d__1, which is also the part of Rhino's message that the method call failed.

Is this is related to late evaluation? Is there a reasonably elegant way to test this more rigorously?

I would also like to fully understand the intermediate evaluation better, which looks a lot like the method that assembled it (in the facade code above).


Solution:1

Are the Facade objects both the same in the test and in the object under test -- i.e., are you injecting the Facade object as well? If the objects are different, this would cause the problem that you are seeing. If they are the same, then you could either realize the enumeration in the method (use ToList()).


Solution:2

Rhino mocks works perfectly, although it isn't always able to know why you've used the wrong syntax or constraints :-)

The way to check an IEnumerable argument for equality is just use the following inline constraint:

Arg<T>.List.Equal(yourList)  

Here's a full example:

[Test]  public void NonProjectMatrix_Injection_IsLoaded()  {      _nonProjectMatrix = MockRepository.GenerateMock<ITimeSheetMatrixWidget>();        var dtos = _facade.NonProjectDtos;      nonProjectMatrix.Expect(x => x.Load(Arg<IEnumerable<DynamicDisplayDto>>.List.Equal(dtos))).Return(dtos.Count());            new MatrixEntryService(_facade, _projectMatrix, _nonProjectMatrix, _totalMatrix);            _nonProjectMatrix.VerifyAllExpectations();      }  

So the issue really had nothing to do with deferred execution. Rhino was just spitting out all it knew about a call not being made the way I told it to expect it, and that's how the IEnumerable looked at the time of the expectation failure.

Cheers..


Solution:3

I just want to address your late evaluation concern:

Both Cast and Concat (and many System.Linq.Enumerable methods) return instances that comply with IEnumerable<T>. The actual results of enumerating these instances are deferred. Why are these results deferred and not eagerly determined?

List<int> firstThreePrimes = Enumerable.Range(0, 1000000)    .Where(i => isPrime(i))    .Take(3)    .ToList();  

ToList enumerates the IEnumerable<int> result from Take(3). No numbers are generated by Range, filtered by Where or limited by Take until ToList enumerates this result.

If the results from Range were eagerly determined, we would generate 1000000 ints.

If the results from Where were eagerly determined, then the runtime would have to send all of those 1000000 ints to our isPrime method.

Instead, the call sequence looks like this.

Range  return from Range  Where  return from Where  Take  return from Take  ToList    GetEnumerator(Take)      GetEnumerator(Where)        GetEnumerator(Range)          return 0        isPrime(0) is false          return 1        isPrime(1) is false          return 2        isPrime(2) is true      2 is the first result    List.Add(2)          return 3        isPrime(3) is true      3 is the second result    List.Add(3)          return 4        isPrime(4) is false          return 5        isPrime(5) is true      5 is the third result    List.Add(5)      break;  

This is easy to confirm with a debugger. Just step through and watch the calls to isPrime.

From examining your code, it looks like _assembler.ToActivityDtoCollection probably enumerates the result and you probably aren't experiencing deferred execution.


Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »