首页 > 解决方案 > Assert.Throws 方法没有捕捉到预期的异常

问题描述

我正在尝试测试一个非常简单的功能。它返回包含某些指定数字的数字。如果第一个参数是null,它会抛出ArgumentNullException

不幸的是,Assert.Throws没有抛出预期的异常并且测试失败。当我尝试调试测试时,它不会进入我的方法。与ArgumentException.

只有最后两个测试失败,其他测试成功。

我要测试的功能:

    /// <summary>
    /// Filter given numbers and return only numbers containing the specified digit.
    /// </summary>
    /// <param name="numbers">The numbers to be filtered.</param>
    /// <param name="digit">The digit which should be found.</param>
    /// <returns>Numbers that contains the digit.</returns>
    /// <exception cref="ArgumentException"> Thrown if the digit value isn't between 0 and 9.</exception>
    /// <exception cref="ArgumentNullException"> Thrown if numbers are null.</exception>
    public static IEnumerable<int> FilterDigits(IEnumerable<int> numbers, byte digit)
    {
        if (numbers == null)
        {
            throw new ArgumentNullException();
        }

        foreach (int number in numbers)
        {
            if (number.ContainsDigit(digit))
            {
                yield return number;
            }
        }
    }

    /// <summary>
    /// Check whether the number contains the given digit.
    /// </summary>
    /// <param name="number">The number which can contain the digit.</param>
    /// <param name="digit">The digit to be found.</param>
    /// <returns>True if the number contains the digit, else false.</returns>
    /// <exception cref="ArgumentException"> Thrown if the digit value isn't between 0 and 9.</exception>
    /// <example> ContainsDigit(10, 1) -> true </example>
    /// <example> ContainsDigit(10, 2) -> false </example>
    private static bool ContainsDigit(this int number, byte digit)
    {
        if (!char.TryParse(digit.ToString(), out char digitChar))
        {
            throw new ArgumentException("The digit should be from 0 to 9.");
        }

        string numberString = number.ToString();

        foreach (char ch in numberString)
        {
            if (ch == digitChar)
            {
                return true;
            }
        }

        return false;
    }

我的测试课:

[TestFixture]
public class DigitsFilterTests
{
    [TestCase(new int[] { 1, 4, 23, 346, 7, 23, 87, 71, 77 }, 7, ExpectedResult = new int[] { 7, 87, 71, 77 })]
    [TestCase(new int[] { 345, 4, 0, 90, 709 }, 0, ExpectedResult = new int[] { 0, 90, 709})]
    public IEnumerable<int> FilterDigits_NumbersContainDigit(int[] numbers, byte digit)
        => DigitsFilter.FilterDigits(numbers, digit);

    [TestCase(new int[] { 1, 4, 222, 9302 }, 7, ExpectedResult = new int[] { })]
    [TestCase(new int[] { 345, 4, 354, 25, 5 }, 0, ExpectedResult = new int[] { })]
    public IEnumerable<int> FilterDigits_NumbersNotContainDigit(int[] numbers, byte digit)
        => DigitsFilter.FilterDigits(numbers, digit);

    [TestCase(new int[] { }, 0, ExpectedResult = new int[] { })]
    public IEnumerable<int> FilterDigits_EmptyList(int[] numbers, byte digit)
        => DigitsFilter.FilterDigits(numbers, digit);

    [Test]
    public void FilterDigits_NullNumbers_ArgumentNullException()
        => Assert.Throws<ArgumentNullException>(() => DigitsFilter.FilterDigits(null, 5));

    [Test]
    public void FilterDigits_InvalidDigit_ArgumentException()
        => Assert.Throws<ArgumentException>(() => DigitsFilter.FilterDigits(new int[] { }, 10));
}

标签: c#nunit

解决方案


您的方法是使用yield return. 棘手的是,除非您枚举它,否则实际上什么都不会发生

因此,您必须确保您的测试枚举内容:

    [Test]
    public void FilterDigits_NullNumbers_ArgumentNullException()
        => Assert.Throws<ArgumentNullException>(() => DigitsFilter.FilterDigits(null, 5).ToList());

ContainsDigit此外,无论哪种方式,您的第二次测试都会失败,因为如果numbers为空,您将无法到达。

如果要修复方法内部的行为,则需要将其分为两部分:

public static IEnumerable<int> FilterDigits(IEnumerable<int> numbers, byte digit)
{
    if (numbers == null)
    {
        throw new ArgumentNullException();
    }

    return FilterDigitsImpl(numbers, digit);
}

private static IEnumerable<int> FilterDigitsImpl(IEnumerable<int> numbers, byte digit)
{
    foreach (int number in numbers)
    {
        if (number.ContainsDigit(digit))
        {
            yield return number;
        }
    }
}

如果您的 C# 版本足够新,则可以使用本地函数合并这两种方法:

public static IEnumerable<int> FilterDigits(IEnumerable<int> numbers, byte digit)
{
    if (numbers == null)
    {
        throw new ArgumentNullException();
    }

    IEnumerable<int> FilterDigitsImpl()
    {
        foreach (int number in numbers)
        {
            if (number.ContainsDigit(digit))
            {
                yield return number;
            }
        }
    }

    return FilterDigitsImpl();
}

推荐阅读