Strange behavior with NUnit, ExpectedException & yield return
Asked Answered
S

1

9

I have a strange behavior in a tests where I want to test that an exception is thrown when null is passed in as a parameter. When I run the test I get from NUnit:

    System.ArgumentNullException was expected
    -- Exception doesn't have a stack trace -- 

My test:

[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void Should_not_retrieve_any_fields_when_file_is_null()
{
    _creator.CreateFields(null);
}

My implementation:

public IEnumerable<ImportField> CreateFields(HttpPostedFileBase file)
{
    if (file == null) throw new ArgumentNullException("file");

    using (var reader = new StreamReader(file.InputStream))
    {
        var firstLine = reader.ReadLine();
        var columns = firstLine.Split(new[] { ',' });

        for (var i = 0; i < columns.Length; i++)
        {
            yield return new ImportField(columns[i], i);
        }
    }
}

Is there a logical explanation to this behavior and should I make my implementation differently?

Spirituous answered 26/11, 2011 at 12:46 Comment(0)
E
10

The reason you're getting this behaviour is because of the yield keyword. When using yield, the compiler will generate a class for the method with the yield in it. When calling that method, control is unconditionally returned to back to the caller. Nothing in your method is actually executed before it is need.

If you extract your using statement into a separate method and return the result, your test will pass. Or you can store the result to a variable in your test, and for example call "ToList()" on it.

    public IEnumerable<ImportField> CreateFields(HttpPostedFileBase file)
    {
        if (file == null) throw new ArgumentNullException("file");

        return ExtractFromFile(file);
    }

    private IEnumerable<ImportField> ExtractFromFile(HttpPostedFileBase file)
    {
        using (var reader = new StreamReader(file.InputStream))
        {
            var firstLine = reader.ReadLine();
            var columns = firstLine.Split(new[] { ',' });

            for (var i = 0; i < columns.Length; i++)
            {
                yield return new ImportField(columns[i], i);
            }
        }
    }
Editheditha answered 26/11, 2011 at 19:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.