简体   繁体   English

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

[英]Assert.Throws method doesn't catch the expected exception

I'm trying to test a really simple function. 我正在尝试测试一个非常简单的功能。 It returns numbers which contain some specified digit. 它返回包含某些指定数字的数字。 If the first argument is null , it'll throw ArgumentNullException . 如果第一个参数为null ,它将抛出ArgumentNullException

Unfortunately, Assert.Throws says, that the expected exception isn't thrown and the test fails. 不幸的是, Assert.Throws说,没有抛出预期的异常,测试失败。 When I'm trying to debug the test, it doesn't step into my method. 当我尝试调试测试时,它不会进入我的方法。 The same thing with ArgumentException . ArgumentException相同的事情。

Only the two last tests fail, others are successful. 只有最后两次测试失败,其他测试失败。

My function to be tested: 我要测试的功能:

    /// <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;
    }

My test 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));
}

Your method is an enumerable built using yield return . 您的方法是使用yield return构建的可枚举。 What's tricky about it is that nothing will actually happen unless you enumerate it . 它的棘手之处在于除非你枚举它,否则什么都不会发生

So you must make sure that your test enumerates the contents: 因此,您必须确保您的测试枚举内容:

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

Also, your second test will fail either way because you won't reach ContainsDigit if numbers is empty. 此外,您的第二次测试将失败,因为如果numbers为空,您将无法访问ContainsDigit

If you want to fix the behavior inside of the method, you need to cut it into two: 如果要修复方法内部的行为,则需要将其切割为两个:

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

If your version of C# is recent enough, you can merge both methods using local functions: 如果您的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();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM