方法应该抛出异常,但它不会

时间:2014-06-27 08:52:23

标签: c# unit-testing mstest

我写了一个小的extensionmethod,它在任何IEnumerable中找到给定字符串的索引。

public static IEnumerable<int> FindIndexesOf(this IEnumerable<string> itemList, string indexesToFind)
{
    if (itemList == null)
        throw new ArgumentNullException("itemList");
    if (indexesToFind == null)
        throw new ArgumentNullException("indexToFind");

    List<string> enumerable = itemList as List<string> ?? itemList.ToList();
    for (int i = 0; i < enumerable.Count(); i++)
    {
        if (enumerable[i] == indexesToFind)
            yield return i;
    }
}

如上所示,如果itemList为null,则抛出ArgumentNullException。干净利落。

在上面的方法上运行我的unittest时,我期望和ArgumentNullException类型的异常,因为itemList为null。但是,测试结果是错误的,因为没有异常被抛出。

怎么可能?逻辑似乎很清楚。请参阅下面的测试。

[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void FindIndexesOfTest2()
{
    string[] items = null;
    IEnumerable<int> indexes = items.FindIndexesOf("one");
}

我的逻辑在哪里出错;为什么不抛出ArgumentNullException?

1 个答案:

答案 0 :(得分:4)

问题是使用yield的调查员被懒惰地评估。

由于您没有对返回的集合进行迭代,因此该方法实际上并未执行。

正确的方法是将方法拆分为两个:

public static IEnumerable<int> FindIndexesOf(this IEnumerable<string> itemList, string indexesToFind)
{
    if (itemList == null)
        throw new ArgumentNullException("itemList");
    if (indexesToFind == null)
        throw new ArgumentNullException("indexToFind");

    return FindIndexesOfImpl(itemList, indexesToFind);    
}

private static IEnumerable<int> FindIndexesOfImpl(this IEnumerable<string> itemList, string indexesToFind)
{
    List<string> enumerable = itemList as List<string> ?? itemList.ToList();
    for (int i = 0; i < enumerable.Count(); i++)
    {
        if (enumerable[i] == indexesToFind)
            yield return i;
    }
}

这里第一个方法将在你调用它时执行,并返回一个懒惰评估的枚举器,它不会被迭代,直到你迭代它。

虽然,我建议你在这里改变后一种方法,以便真正懒惰地评估。事实上该方法缓存整个itemList只是为了能够使用索引是不必要的,事实上你可以在没有它的情况下重写它:

public static IEnumerable<int> FindIndexesOfImpl(this IEnumerable<string> itemList, string indexesToFind)
{
    var index = 0;
    foreach (var item in itemList)
    {
        if (item == indexesToFind)
            yield return index;
        index++;
    }
}

你也可以使用LINQ扩展方法来做到这一点,虽然这涉及为每个元素构建一个临时对象,不确定它是否值得,我会选择上面的那个:

public static IEnumerable<int> FindIndexesOfImpl(this IEnumerable<string> itemList, string indexesToFind)
{
    return itemList
        .Select((item, index) => new { item, index })
        .Where(element => element.item == indexesToFind)
        .Select(element => element.index);
}

使用最后一种方法,您可以将其移回主方法,因为您不再使用yield

public static IEnumerable<int> FindIndexesOf(this IEnumerable<string> itemList, string indexesToFind)
{
    if (itemList == null)
        throw new ArgumentNullException("itemList");
    if (indexesToFind == null)
        throw new ArgumentNullException("indexToFind");

    return itemList
        .Select((item, index) => new { item, index })
        .Where(element => element.item == indexesToFind)
        .Select(element => element.index);
}
相关问题