33

我想编写一个 TestNG 测试以确保在特定条件下引发异常,如果未引发异常,则测试失败。有没有一种简单的方法可以做到这一点而无需创建额外的布尔变量?

关于此主题的相关博客文章:http: //konigsberg.blogspot.com/2007/11/testng-and-expectedexceptions-ive.html

4

7 回答 7

39

@Test(expectedExceptions)对于最常见的情况很有用:

  • 您希望抛出一个特定的异常
  • 您需要该异常的消息以包含特定的单词

expectedException根据文档,如果没有抛出,测试将失败:

测试方法预期抛出的异常列表。如果没有抛出异常或与此列表中的异常不同,则此测试将被标记为失败。

@Test(expectedExceptions)以下是一些不够用的场景:

  • 您的测试方法有多个语句,预计只有其中一个会抛出
  • 您正在抛出自己类型的异常,您需要确保它符合某个标准

在这种情况下,您应该恢复到传统的(TestNG 前)模式:

try {
  // your statement expected to throw
  fail();
}
catch(<the expected exception>) {
  // pass
}
于 2010-09-09T17:26:10.130 回答
15

使用@Test注释检查预期的异常。

@Test(
    expectedExceptions = AnyClassThatExtendsException.class,
    expectedExceptionsMessageRegExp = "Exception message regexp"
)

或者,如果您不想检查异常消息,仅以下就足够了

@Test(expectedExceptions = AnyClassThatExtendsException.class)

这样,您不需要使用丑陋的 try catch 块,只需在测试中调用您的异常抛出方法。

于 2015-12-02T20:01:17.820 回答
2

我不得不不同意这篇关于所采用的测试技术性质的文章。该解决方案采用门,以验证测试是否应该在中间阶段成功或失败。

在我看来,最好使用Guard Assertions,特别是对于这样的测试(假设测试不会变得冗长和复杂,这本身就是一种反模式)。使用警卫断言迫使您以下列任一方式设计 SUT:

  • 设计方法本身以在结果中提供有关调用是否通过或成功的足够信息。有时,这无法做到,因为设计者的意图是不返回结果,而是抛出异常(这可以在第二种情况下处理)。
  • 设计 SUT,以便在每次重要的方法调用后验证其状态。

但在我们考虑上述可能性之前,请再次查看以下代码段:

plane.bookAllSeats();
plane.bookPlane(createValidItinerary(), null);

如果打算测试 bookPlane() 并验证该方法的执行,最好将 bookAllSeats() 放在夹具中。在我的理解中,调用 bookAllSeats() 相当于设置 SUT 以确保调用 bookPlane() 失败,因此有一个夹具来做同样的事情将使测试更具可读性。如果意图不同,我建议在每次转换后测试状态(就像我通常在功能测试中所做的那样),以帮助查明失败的原始原因。

于 2010-09-09T14:25:12.507 回答
2

如果您使用的是 java 7 和 testng,这可以用于 java 8,您也可以使用 lambda 表达式

class A implements ThrowingRunnable{


            @Override
            public void run() throws AuthenticationFailedException{
                spy.processAuthenticationResponse(mockRequest, mockResponse, authenticationContext);
            }
        }
        assertThrows(AuthenticationFailedException.class,new A());
于 2019-05-15T08:59:56.560 回答
0

为什么不使用链接到的博客文章中提到的 try/fail/catch 模式?

于 2010-09-09T14:11:06.247 回答
0

catch-exception可能提供了测试预期异常所需的一切。

于 2012-06-22T16:36:43.727 回答
0

我创建了一个由数组支持的自定义 Stack 数据结构。当堆栈已满并且您仍尝试将数据 push() 入堆栈时,push() 方法会引发自定义异常。你可以这样处理

public class TestStackDataStructure {
    //All test methods use this variable.
    public Stack<String> stack;//This Stack class is NOT from Java.

    @BeforeMethod
    public void beforeMethod(){
    //Don't want to repeat this code inside each test, especially if we have several lines for setup.
        stack = new Stack<>(5);
    }

    @Test
    public void pushItemIntoAFullStack(){
        //I know this code won't throw exceptions, but what if we have some code that does ?
        IntStream.rangeClosed(1,5).mapToObj(i -> i + "").forEach(stack::push);

        try{
            stack.push("6");
            Assert.fail("Exception expected.");
        }catch (StackIsFullException ex) {
            // do nothing;
        }
    }

    //Other tests here.
}

或者,您可以按照此处的建议更改您的 api :

@Test
public void pushItemIntoAFullStack(){
    IntStream.rangeClosed(1,5).mapToObj(i -> i + "").forEach(stack::push);
    Assert.assertFalse( stack.push("6"), "Expected push to fail." );
}

如果操作通过或失败,我更新了 push 方法以返回 true 或 false,而不是返回 void。Java Stack .push(item) 返回您尝试插入的元素而不是 void 。我不知道为什么。但是,它也从 Vector 继承了一个类似的方法 addElement(item),该方法返回 void。

我看到让 push(item) 返回布尔值或 void 的一个小缺点是你被这些返回类型所困扰。如果您返回 Stack ,那么您可以像这样方便地编写代码stack.push(1).push(2).push(3).pop()。但是,我不知道需要多久编写一次这样的代码。

类似地,我的 pop() 方法用于返回泛型类型“T”并用于在堆栈为空时引发异常。我将其更新为返回Optional<T>

@Test
public void popEmptyStack(){
    Assert.assertTrue(stack.pop().isEmpty());
}

我想我现在摆脱了笨重的 try-catch 块和 TestNg 预期异常。希望我的设计现在很好。

于 2020-04-05T22:50:16.487 回答