检查单元测试中是否抛出了参数异常

5
我正在为一个接受三个参数的构造函数编写单元测试。这些数字应该是0或更高,并且现在我正在编写一个构造函数的单元测试,如果不是这种情况,就会抛出异常。
我无法确定在“Assert”后面写什么来确定测试是否通过,以便在将非法数字传递给构造函数时测试通过。 提前致谢。
编辑:我正在使用MSTest框架。
   public void uniqueSidesTest2()
    {
        try {
            Triangle_Accessor target = new Triangle_Accessor(0, 10, 10);
        }
        catch (){
            Assert // true (pass the test)
            return;
        }

        Assert. // false (test fails)
    }

// 从代码中...

    public Triangle(double a, double b, double c) {
        if ((a <= 0) || (b <= 0) || (c <= 0)){
            throw new ArgumentException("The numbers must higher than 0.");
        }
        sides = new double[] { a, b, c };
    }

我不确定在标准的MS测试中是否可以,但在NUnit中,您需要注释测试以期望异常。您仍然会收到错误消息,但它不会被视为失败,例如:ExpectedException(typeof(ArgumentException))] - dougajmcdonald
如何在 C++ 中使用断言(assert)进行单元测试并验证一个异常是否被抛出? - ken2k
6个回答

10
首先,你应该抛出一个ArgumentOutOfRangeException而不是仅抛出ArgumentException
其次,你的单元测试应该期望抛出异常,像这样:
[ExpectedException(typeof(ArgumentOutOfRangeException))]
public static void MyUnitTestForArgumentA()
{
    ...
}

因此,您需要创建单独的单元测试--每个参数一个--以测试方法在参数超出范围时是否会抛出正确的异常。


谢谢!我已经在代码中更改为ArgumentOutOfRangeException,但是当我添加[ExpectedException(typeof(ArgumentOutOfRangeException))]时,我收到了以下错误消息:“Error 3 The type or namespace name 'ArgumentOutOfRangeException' could not be found (are you missing a using directive or an assembly reference?)”? - holyredbeard
它在 System 命名空间中,所以如果你能找到 ArgumentException,你也应该自动找到 ArgumentOutOfRangeException。你确定你的拼写完全正确吗? - Roy Dictus
请参阅http://msdn.microsoft.com/en-us/library/system.argumentoutofrangeexception.aspx。 - Roy Dictus
你是否在类文件中缺少了 using System;?该异常位于 mscorlib.dll 和 System 命名空间中。 - Anthony Pegram

4
不需要使用try catch块。使用NUnit或MSTest框架,您可以在测试方法声明上使用属性来指定您期望一个异常。
MSTest
 [TestMethod]
 [ExpectedException(typeof(ArgumentException))]
 public void uniqueSidesTest2()

我建议不要使用该属性,因为您不知道哪个方法负责抛出异常。 - Toni Parviainen
@Toni,能否详细说明一下,因为我没有理解你的思路。我可能唯一遇到的问题是异常可能不够具体,方法名称也不是很好,但这两个问题都是直接从问题中继承的特征。但方法是可靠的。 - Anthony Pegram
如果您使用ExpectedException,您将不知道测试中哪一行抛出了异常。简单示例。在这个例子中,您不知道哪个方法抛出了异常,因此即使错误的东西抛出了异常,测试也可能会通过。 - Toni Parviainen
@Toni,那不是一个有效的单元测试,它做了太多的事情,测试了超过一个东西。而且它也不能反映出这里的情况,问题非常明显只测试构造函数。 - Anthony Pegram
@AnthonyPegram 我的例子不好。我试图表达的观点是,如果你使用ExpectedException属性,你无法确定异常是否从正确的方法中抛出。你可能有一些设置或测试特定的使用场景。并不是每个单元测试只测试一个方法。你是对的,在这种特殊情况下(仅测试构造函数)ExpectedException的使用是有效的。太多时候,了解它的人滥用它,并且在一些集成测试甚至使用它。 - Toni Parviainen

2

这可能不是最好的解决方案,但如果我要测试确保抛出异常,我会采取以下步骤:

public void uniqueSidesTest2()
{
    try {
        Triangle_Accessor target = new Triangle_Accessor(0, 10, 10);
        Assert.Fail("An exception was not thrown for an invalid argument.");
    }
    catch (ArgumentException ex){
        //Do nothing, test passes if Assert.Fail() was not called
    }
}

如果你的构造函数调用应该抛出一个错误,那么如果它到达第二行(Assert.Fail()这一行),那么你就知道它没有正确地抛出异常。


1
你至少应该捕获适当的异常,否则如果抛出任何异常,你将会成功,这并不完全正确。 - Austin Salonen
非常正确,我只是想尝试给出一个基本的解释,说明我过去是如何做的,并且只是复制/粘贴了他的代码。我会进行一些编辑。感谢您提醒我。 - jamesmillerio

2
如果你没有nunit(或其他已经内置此支持的框架),你可以使用以下类型的辅助方法。
 public static void ThrowsExceptionOfType<T>(Action action) where T: Exception
    {
        try
        {
            action();
        }
        catch (T)
        {
            return;
        }
        catch (Exception exp)
        {
            throw new Exception(string.Format("Assert failed. Expecting exception of type {0} but got {1}.", typeof(T).Name, exp.GetType().Name));
        }

        throw new Exception(string.Format("Assert failed. Expecting exception of type {0} but no exception was thrown.", typeof(T).Name));
    }

您的测试将如下所示:

您的测试将如下所示


AssertHelper.ThrowsExceptionOfType<ArgumentException>( 
    () => 
    {
        new Triangle_Accessor(0, 10, 10);
    });

这也是我的做法。我还使用了一个带有输出参数的辅助方法重载,该参数被设置为抛出的异常。这样可以在需要时检查异常。 - joelsand
public static void ThrowsException<TException>(out TException thrownException, Action action) where TException : Exception { try { action.Invoke(); // 如果没有抛出异常,则失败 Assert.Fail("Assert.ThrowsException 失败 - 未抛出预期的异常: " + typeof(TException).ToString()); thrownException = null; } catch (TException ex) { thrownException = ex; } } - joelsand

0

catch 中,您不需要 Assert(但您可能希望捕获更具体的异常,例如 ArgumentException)。

要始终失败,可以使用 Assert.Fail


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接