How can i mock or test my deferred evaluation/execution functionality?
Asked Answered
T

2

6

I have what could be seen as a bizarre hybrid of IQueryable<T> and IList<T> collections of domain objects passed up my application stack. I'm trying to maintain as much of the 'late querying' or 'lazy loading' as possible. I do this in two ways:

  1. By using a LinqToSql data layer and passing IQueryable<T>s through by repositories and to my app layer.
  2. Then after my app layer passing IList<T>s but where certain elements in the object/aggregate graph are 'chained' with delegates so as to defer their loading. Sometimes even the delegate contents rely on IQueryable<T> sources and the DataContext are injected.

This works for me so far.

What is blindingly difficult is proving that this design actually works. Ie. If i defeat the 'lazy' part somewhere and my evaluation/execution happens early then the whole thing is a waste of time. I'd like to be able to TDD this somehow.

I don't know a lot about delegates or thread safety as it applies to delegates acting on the same source. I'd like to be able to mock the DataContext and somehow trace both methods of deferring (IQueryable<T>'s SQL and the delegates) the loading so that i can have tests that prove that both functions are working at different levels/layers of the app/stack.

As it's crucial that the deferring works for the design to be of any value, i'd like to see tests fail when i break the design at a given level (separate from the live implementation). Is this possible?

Thrashing answered 18/5, 2010 at 5:59 Comment(0)
B
4

At morelinq, we have a so called "breaking sequence" to test that. Basically, it is an enumerator that will throw an exception whenever it is enumerated.

It can be as easy as:

internal sealed class BreakingSequence<T> : IEnumerable<T>
{
    public IEnumerator<T> GetEnumerator()
    {
        throw new InvalidOperationException();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

A test for it looks like this:

   [Test]
    public void XyzIsLazy()
    {
        var source = BreakingSequence<EntityClass>().AsQueryable();

        // inject it as your query source wherever appropriate
        var query = source.Where(x=> true);
        // does not throw, where does not enumerate the sequence
    }
Basildon answered 24/5, 2010 at 9:56 Comment(3)
Sounds like a start. How about when i have like a LazyItem<T> that uses delegates to trigger the evaluation of the encapsulated item? #1670107Thrashing
@cottsak: You can set the delegate to be a breaking delegate (a delegate that raises an exception).Basildon
Sounds like a good idea. Perhaps i was after a more complex solution when it wasn't necessary.Thrashing
E
2

I'm going to answer in a similar vein to what Johannes Rudolph answered. It sounds like you're thinking the right thing, wanting to have a test for something important that would be difficult to trace if it fails.

I would definitely suggest using a mock for this purpose. Johannes suggested an object that throws exception when it is enumerated. Since your object is templated, I believe you should be able to use whatever object you want. A mocking framework, such as Rhino.Mocks (free) or TypeMock Isolator (expensive), may be very helpful. I highly recommend looking into mocking frameworks if you haven't already.

With a mock in hand, you could evaluate that operations should take place in a certain order when you run certain test code. You could program your mock to record what operations take place and then check the recording at the end of the test.

Etom answered 28/5, 2010 at 14:22 Comment(3)
+1 for the recording suggestion. i have used mocks very lightly. i have seen the "recording" patterns and that might be a great way to bring together a whole string of complex operations in a test. thanks.Thrashing
I did evaluate TypeMock Isolator, but decided that it is too expensive to use for the few tests that I do. However, if you're going to test a lot of things like this, you may find that it often makes setting up your tests soooooo much easier than it would be with the other frameworks.Etom
Yeh i know Rhino Mocks does the recording thing but Moq suggests that "recording" is not that great and it's designed not to adhere to this approach - so maybe i'll look into how Moq might satisfy the above. Maybe that will shed some inspiration on how to test this.Thrashing

© 2022 - 2024 — McMap. All rights reserved.