Let’s have a quick chat about deferring execution. Take a look at the following test:

[SetUp]
    public void SetUp() {
        _mockery = new MockRepository( );
        _mapper = _mockery.DynamicMock< IXmlToBookMapper >( );
        _xmlGateway = _mockery.DynamicMock< IXmlGateway >( );
    }

    public IBooksGateway CreateSUT() {
        return new BooksGateway( _mapper, _xmlGateway );
    }

    [Test]
    public void Should_leverage_xml_bank_to_retrieve_xml() {
        using ( _mockery.Record( ) ) {
            Expect.Call( _xmlGateway.AllElementsNamed( "Book" ) ).Return( new List< IXmlElement >( ) );
        }

        using ( _mockery.Playback( ) ) {
            CreateSUT( ).LoadAllBooksFromStorage( );
        }
    }

When I run this test I get the following output:

It says that the expectation set on the xml gateway was never satisfied. So the call to the method “AllElementsNamed()” with an input parameter with a value of “Book” was not made.

Let’s take a look at the implementation that failed this test.

public class BooksGateway : IBooksGateway {
        public BooksGateway( IXmlToBookMapper mapper, IXmlGateway xmlGateway ) {
            _mapper = mapper;
            _xmlGateway = xmlGateway;
        }

        public IEnumerable< IBook > LoadAllBooksFromStorage() {
            foreach ( IXmlElement element in _xmlGateway.AllElementsNamed( "Book" ) ) {
                yield return _mapper.MapFrom( element );
            }
        }

        private readonly IXmlToBookMapper _mapper;
        private readonly IXmlGateway _xmlGateway;
    }

Can you spot the error? Really?

There is no error, the reason this test fails is because of something that C# 2.0 offered for free that very few people actually talk about, and that’s deferred execution. The iteration through the loop never occurs because the client of the BooksGateway component never actually begins iterating. In this case the client component is our unit test.

CreateSUT( ).LoadAllBooksFromStorage( );

The above line never actually starts to walk the underlying collection therefore causes the expectation violation to occur. Traversal through the collection is put off until the last possible moment necessary. What this also means is that each time the traversal through the collection re-starts it will actually rebuild the internal collection to walk through. This works great for immutable types but can cause a bit of a head ache with types that change through out its lifetime, since new instances are brought back out of persistence. In this case, each time we walk the underlying collection we’re actually re-reading the books from an xml file.

If we re-write the test like this…

[Test]
    public void Should_leverage_xml_bank_to_retrieve_xml() {
        using ( _mockery.Record( ) ) {
            Expect.Call( _xmlGateway.AllElementsNamed( "Book" ) ).Return( new List< IXmlElement >( ) );
        }

        using ( _mockery.Playback( ) ) {
            foreach ( IBook book in CreateSUT( ).LoadAllBooksFromStorage( ) ) {
                Console.Out.WriteLine( book.Name( ) );
            }
        }
    }

The test now passes…

comments powered by Disqus