c# - 方法应该抛出异常但它没有

标签 c# unit-testing mstest

我写了一个小的扩展方法,它在任何 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。干净利落。

在上述方法上运行我的单元测试时,我预计会出现类型为 ArgumentNullException 的异常,因为 itemList 为 null。但是,测试结果为假,因为没有抛出异常。

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

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

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

最佳答案

问题是使用 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);
}

关于c# - 方法应该抛出异常但它没有,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24447727/

相关文章:

c# - 如何在 ASP.NET Core 1.0 中为 Web 应用程序显示 LoggerFactory 日志控制台?

c# - myCustomDictionary.Values 应该返回什么类型?

c# - 在 ClosedXML 中,是否有从列标题名称中获取列字母的方法?

python - 如何对不返回任何内容的函数进行单元测试?

python - 如何测试函数是否运行正常或返回特定值

c# - Specflow 和 HttpSelfHostServer

unit-testing - MSTest:如何增加测试时间

c# - 您使用哪些信息/视频来源来学习使用 C# 进行 Web 开发的新技术?

visual-studio-2010 - MSTest将单元线程设置为MTA

c# - 任何人都可以建议使用最小起订量框架的逐步示例