Question

I'm trying to write a unit test to check for parsing errors. I'm streaming data in from a file, parsing it and returning the parsed result with yield return, then passing it to a data layer to bulk insert.

I'm having trouble mocking out the call to the data layer. Because it's mocked it never actually enumerates the values from the yield return and thus my parsing method never executes.

public class Processor
{
    public IUnityContainer Container { get; set; }

    public void ProcessFile(Stream stream)
    {
        var datamanager = Container.Resolve<IDataManager>();                                            
        var things = Parse(stream);        
        datamanager.Save(things);                                
    }

    IEnumerable<string> Parse(Stream stream)
    {
        var sr = new StreamReader(stream);
        while (!sr.EndOfStream)
        {
            string line = sr.ReadLine();
            // do magic
            yield return line;
        }
    }
}

I tried something like this which obviously doesn't work.

[TestMethod]        
[ExpectedException(typeof(ApplicationException))]
public void ProcessFile_InvalidInput_ThrowsException()
{
    var mock = new MockRepository();

    var stream = new MemoryStream();
    var streamWriter = new StreamWriter(stream);                
    streamWriter.WriteLine("\\:fail");
    streamWriter.Flush();
    stream.Position = 0;

    var datamanager = mock.Stub<IDataManager>();                        
    TestContainer.RegisterInstance(datamanager);

    var repos = new ProcessingRepository();
    TestContainer.BuildUp(repos);

    using (mock.Record())
    {                         
        Expect.Call(file.InputStream).Return(stream);                            
        Expect.Call(delegate() { repos.Save(new List<string>()) }).IgnoreArguments();
    }
    using (mock.Playback())
    {
        repos.ProcessFile(stream);
    }
}
Was it helpful?

Solution

One optimal solution would be to put the stuff that happens in "//do magic" in a separate method so it can be unit tested in isolation -- without the need of being called from inside a while loop that is processing a StreamReader.

The issue you're seeing is due to lazy evaluation of the enumeration. Since none of your test code is actually enumerating the "things", the state machine that is built up "behind the scenes" to handle the iterator block is never processed.

You'll need to cause the items to be enumerated in order to actually execute the logic in the Parse method. You can do this my using Rhino.Mocks "WhenCalled" method (I'm showing the AAA syntax since I don't recall how to use the record/replay semantics):

NOTE: This is untested code

datamanager.Stub(d => d.Save(null)).IgnoreArguments().WhenCalled(m => int count = ((IEnumerable<string>)m.Arguments[0]).Count());

What happens is that when the Save method on your stub is called, the "WhenCalled" is passed a parameter (m) which contains information about the method called. Grab the first argument (things), cast it to IEnumerable<string> and get its count. This will force an evaluation of the enumerable.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top