Simple NUnit test fail because exception is not thrown ( out of the test is thrown)

Viewed 134

I'm creating an extension generic method Apply, what it does is not important, the major problem is that I cant understand how to test it, a simple test like ApplyOnNullFunctionThrow fail to say that no exception is thrown, but if I run a "manual test" from main it throws

The main:

class Program
{
    static void Main(string[] args)
    {
        foreach (var i in first.Apply(second,f)) Console.WriteLine("{0}", i);
        Console.ReadLine();
    }
}

The extension method:

public static class Applier
{
    public static IEnumerable<T> Apply<T>(this IEnumerable<T> first, IEnumerable<T> second,Func<T,T,T> f)
    {
        if(f == null ) throw new ArgumentNullException();
        if (null==first || null==second) throw new ArgumentNullException();
        var x=second.GetEnumerator();
        
        foreach (var item in first)
        {
           yield return x.MoveNext() ? f(item, x.Current) : f(item, default);

        }

        while (x.MoveNext())
        {
            yield return f(default, x.Current);
        }
            

    }
}

The test:

class ApplierTests
{
    [TestCase(new[]{1,2,3},new[]{10,20,30},null)]
    public void ApplyOnNullFunctionThrow<T>(IEnumerable<T> first, IEnumerable<T> second,Func<T,T,T> f)
    {
        Assert.Throws<ArgumentNullException>(() => first.Apply(second, f));
    }
}
1 Answers

In your Apply method you have yield keyword which means that the elements of the sequence will be returned one at a time. So when you just call the method with yield keyword, under the hood it does nothing, but just returns an iterator (IEnumerable<T>) which you can use in, for example, foreach. The bottom line here is that the code of your original Apply method isn't executed until MoveNext is called on the iterator returned, therefore it doesn't throw the exception. To fix this you may want to have a wrapper which is without yield keyword:

public static IEnumerable<T> Apply<T>(this IEnumerable<T> first, IEnumerable<T> second,Func<T,T,T> f)
{
    if(f == null ) throw new ArgumentNullException();
    if (null==first || null==second) throw new ArgumentNullException();
    return ApplyInternal(first, second, f);
}

private static IEnumerable<T> ApplyInternal<T>(...) {
    var x=second.GetEnumerator();
    
    foreach (var item in first)
    {
       yield return x.MoveNext() ? f(item, x.Current) : f(item, default);

    }

    while (x.MoveNext())
    {
        yield return f(default, x.Current);
    }
}

Instead of creating a private method you also can use C# 7 feature called local functions as alternative

See also, the exception is thrown when you call the method in Main, because you use the result of the method in the foreach statement which is compiled into repeated calls to MoveNext.

Related