Assert.Throws 方法没有捕捉到预期的异常

Assert.Throws method doesn't catch the expected exception

我正在尝试测试一个非常简单的函数。它 returns 包含一些指定数字的数字。如果第一个参数是 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;
    }

我的测试class:

[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));
}

您的方法是使用 yield return 构建的可枚举方法。它的棘手之处在于,除非您枚举它,否则实际上什么都不会发生

因此您必须确保您的测试枚举了以下内容:

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

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

如果你想修复方法内部的行为,你需要把它分成两部分:

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();
}