TDD如何与Exceptions和参数验证一起使用?

时间:2009-11-21 16:51:57

标签: unit-testing debugging exception tdd

我已经到了一个十字路口。我最近写了一个没有TDD的10,000行应用程序(我知道这个错误)。我肯定遇到了很多错误,但现在我想改造项目。这是我遇到的问题。让我们举一个分裂函数的例子:

public int divide (int var1, int var2){
if (var1 == 0 || var2 == 0)
   throw new RuntimeException("One of the parameters is zero");
return var1 / var2;
}

在这种情况下,我抛出一个运行时错误,以便我可以失败,至少发现我的代码在某个地方被破坏了。问题是2倍。首先,我是否在这里正确使用了例外情况?其次,如何编写测试来处理此异常?显然我希望它通过测试,但在这种情况下,它会抛出异常。 不太确定如何解决这个问题。是否有一种不同的方式通常用TDD处理?

由于

5 个答案:

答案 0 :(得分:9)

首先,你的第一个参数(分子)为零可能不应该引发异常。答案应该是零。仅在用户尝试除以零时抛出异常。

其次,有两种方法(使用JUnit)来测试它们应该抛出的异常。第一个“经典”方法:

@Test
public void testForExpectedExceptionWithTryCatch()
        throws Exception {
    try {
        divide (1, 0);
        fail("division by zero should throw an exception!");
    } catch (RuntimeException expected) {
        // this is exactly what you expect so 
        // just ignore it and let the test pass
    }
}

JUnit 4中的新方法使用注释来减少需要编写的代码量:

@Test(expected = RuntimeException.class)
public void testForExpectedExceptionWithAnnotation()
        throws Exception {
    divide (1, 0);
}

此处,由于我们在注释中添加了(expected = RuntimeException.class),因此如果对divide 的调用不投掷RuntimeException,则测试将失败。

答案 1 :(得分:6)

回答你的第一个问题:

如果divide的分母参数很可能为0,那么您不应该使用异常处理来捕获错误。例外情况很昂贵,不应用于控制程序流程。所以你仍然应该检查,但是返回一个错误代码(或者使用一个可空的类型作为返回值),你的调用代码应该检查并正确处理它。

public int? divide (int var1, int var2)
{
    if (var2 == 0)
    {
        return null;  // Calling method must check for this
    }
    return var1 / var2;
}

如果零真的是例外 - 例如他们不应该被传递 - 然后就像现在一样。

回答你的第二个问题:

在检查失败代码的测试方法中,您需要一个异常处理程序:

try
{
    divide (1, 0);
    // If it gets here the test failed
}
catch (RuntimeException ex)
{
    // If it gets here the test passed
}

答案 2 :(得分:0)

我没有回答你的主要问题。

我建议使用ArgumentException代替RuntimeException

编辑:我假设.net:)

答案 3 :(得分:0)

你的问题与语言无关,所以我的回答可能不适用,但是.NET中的NUnit(我也相信JUnit)有一个特定的符号来测试异常。在NUnit中,您的测试将如下所示:

[Test]
[ExpectedException(typeof(RuntimeException))]
public void DivideByZeroShouldThrow()
{
   divide(1,0);
}

如果在执行期间没有抛出正确的异常类型,测试将失败 try / catch方法也有效,并且有其优点(您可以准确地确定异常发生的位置),但最终编写起来非常繁琐。

答案 4 :(得分:0)

第一个问题由ChrisF和Bill the Lizard很好地回答。 我只想添加一个异常测试的替代方案,使用C ++ 11,您可以在测试中直接使用lambda。

Assert::ExpectException<std::invalid_argument>([] { return divide(1,0); }, "Division by zero should throw an exception.");

这相当于:

try
{
 divide(1,0);
 Assert::Fail("Division by zero should throw an exception.");
}
catch(std::invalid_argument)
{
  //test passed
}