开发者

TestNG: How to test for mandatory exceptions?

I'd like to write a TestNG test to make sure an exception is thrown under a specific condition, and fail the test if the exception is not thrown. Is there an easy way to do this without having to create an extra boolean variable?

开发者_StackOverflow社区A related blog post on this subject: http://konigsberg.blogspot.com/2007/11/testng-and-expectedexceptions-ive.html


@Test(expectedExceptions) is useful for the most common cases:

  • You expect a specific exception to be thrown
  • You need the message of that exception to contain specific words

Per the documentation a test will fail if no expectedException is thrown:

The list of exceptions that a test method is expected to throw. If no exception or a different than one on this list is thrown, this test will be marked a failure.

Here are a few scenarios where @Test(expectedExceptions) is not sufficient:

  • Your test method has several statements and only one of them is expected to throw
  • You are throwing your own type of exception and you need to make sure it matches a certain criterion

In such cases, you should just revert to the traditional (pre-TestNG) pattern:

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


Use @Test annotation to check expected exceptions.

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

Or if you don't want to check for exception message, only below is enough

@Test(expectedExceptions = AnyClassThatExtendsException.class)

In that way, you don't need to use ugly try catch block, just invoke you exception-thrower method inside the test.


I have to disagree with the the article on the nature of the testing techniques employed. The solution employs a gate, to verify if the test should succeed or fail in an intermediate stage.

In my opinion, it is better to employ Guard Assertions, especially for such tests (assuming that the test does not turn out to be long-winded and complex, which is an anti-pattern in itself). Using guard-assertions forces you to design the SUT in either of the following ways:

  • design the method itself to provide enough information in the result on whether the invocation passed or succeeded. Sometimes, this cannot be done as the intention of the designer is to not return a result, and instead throw an exception (this can be handled in the second case).
  • design the SUT so that it's state can be verified after each significant method invocation.

But before we consider the above possibilities, have a look at the following snippet again:

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

If the intention is to test bookPlane() and verify for execution of that method, it is better to have bookAllSeats() in a fixture. In my understanding, invoking bookAllSeats() is equivalent to setting up the SUT to ensure that the invocation of bookPlane() fails, and hence having a fixture to do the same would make for a more readable test. If the intention are different, I would recommend testing the state after every transition (as I normally would do in functional tests), to help pinpoint the original cause of failure.


if you are using java 7 and testng this can be used for java 8 you can also use lambda expressions

class A implements ThrowingRunnable{


            @Override
            public void run() throws AuthenticationFailedException{
                spy.processAuthenticationResponse(mockRequest, mockResponse, authenticationContext);
            }
        }
        assertThrows(AuthenticationFailedException.class,new A());


Why don't you use the try/fail/catch pattern mentioned in the blog post you linked to?


catch-exception provides probably everything you need to test for expected exceptions.


I created a custom Stack data structure which is backed by an array. The push() method throws a custom exception when the stack is full and you still try to push() data into the stack. You could handle it 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.
}

Alternately, you could change your api as suggested here :

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

I updated the push method to return true or false if the operation passed or failed, instead of returning void. The Java Stack.push(item) returns the element you tried to insert instead of void. I don't know why. But, it also inherits a similar method addElement(item) from Vector which returns void.

One minor downside I see to making push(item) return a boolean or void is that you are stuck with those return types. If you return Stack instead then you can write code conveniently like this stack.push(1).push(2).push(3).pop(). But, I don't know how often one would have to write code like that.

Similarly, my pop() method used to return a generic type "T" and used to throw an exception if the stack was empty. I updated it to return Optional<T> instead.

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

I guess I am now free of the clunky try-catch blocks and TestNg expectedExceptions. Hopefully, my design is good now.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜