【问题标题】:TestNG: How to test for mandatory exceptions?TestNG:如何测试强制异常?
【发布时间】:2011-04-10 06:54:50
【问题描述】:

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

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

【问题讨论】:

    标签: java unit-testing testng


    【解决方案1】:

    @Test(expectedExceptions) 适用于最常见的情况:

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

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

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

    以下是@Test(expectedExceptions) 不够用的几种情况:

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

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

    try {
      // your statement expected to throw
      fail();
    }
    catch(<the expected exception>) {
      // pass
    }
    

    【讨论】:

    • 有没有比 TestNg 更好地处理这种情况的框架?顺便说一句,有一个像这样的注释 BeforeMethod(runFor = "testMethod123") 是个好主意吗?
    • 我首先使用了您的答案,但在我的代码中替换了它。这是我的答案-stackoverflow.com/a/61050562/1477760 你能复习一下吗?谢谢。
    【解决方案2】:

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

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

    或者如果你不想检查异常信息,只有以下就足够了

    @Test(expectedExceptions = AnyClassThatExtendsException.class)
    

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

    【讨论】:

      【解决方案3】:

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

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

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

      但在我们考虑上述可能性之前,再看一下下面的sn-p:

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

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

      【讨论】:

      • 查看 Christopher 链接到的文章。那篇文章解释了它有什么问题。基本上是你不知道预期的异常是在哪里抛出的。
      • 我对单元测试还是很陌生,所以我不知道守卫断言的概念。感谢您的链接!
      • Vineet,你能看看我的回答吗?我参考了您的答案并选择了答案来得出我的答案。谢谢。 stackoverflow.com/a/61050562/1477760
      【解决方案4】:

      如果您使用的是 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());
      

      【讨论】:

      • 这在许多情况下比接受的答案更好,因为他的方式可以确保哪个调用创建了异常。否则可能会发生之前的调用已经失败。
      【解决方案5】:

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

      【讨论】:

      • 当我最初看到它时,我觉得很尴尬。现在我已经进行了实验,似乎它实际上应该适用于我正在尝试做的事情。
      • TestNG 吹嘘不必这样做。
      【解决方案6】:

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

      【讨论】:

        【解决方案7】:

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

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

        或者,您可以按照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&lt;T&gt;

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-05-24
          • 1970-01-01
          • 2023-04-05
          • 2011-07-09
          • 1970-01-01
          • 2016-02-16
          • 2023-04-10
          相关资源
          最近更新 更多