Java – how to verify whether an exception is thrown

In my unit tests using mockito, I want to verify that NullPointerException is not thrown

public void testNPENotThrown{
    Calling calling= Mock(Calling.class);
    testClass.setInner(calling);
    testClass.setThrow(true);

    testClass.testMethod();

    verify(calling,never()).method();
}

My test set testclass and set the calling object and property so that the method will throw NullPointerException

I verify calling Method () is never called

public void testMethod(){
    if(throw) {
        throw new NullPointerException();
    }

    calling.method();
}

I want a failed test because it throws a NullPointerException, and then I want to write some code to solve this problem

What I notice is that tests always pass because exceptions are never thrown out of the test method

Solution

TL; doctor

>Pre-jdk8: I will recommend old and good try catch blocks. > Post - JDK 8: use assert J or custom Lambdas to represent abnormal behavior

A long story

You can write your own catch block or use JUnit tools (@ test (expected =...) or @ rule expectedexception JUnit rule function)

However, these methods are not so elegant and can not be well mixed with other tools for good readability

>Try catch block you must write the block around the behavior being tested and write assertions in the broken block. It may be good, but many search taht styles will interrupt the reading process of the test In addition, you need to write an assert. At the end of the try block Fail, otherwise the test may miss one side of the assertion; PMD, findbugs or sonar will find such problems. >@ The test (expected =...) function is interesting because you can write less code and then write this test. It is said that it is less prone to coding errors But this approach lacks some aspects

>If the test needs to check other things of the exception, such as cause or message (good exception message is very important, and having precise exception type may not be enough). > In addition, the expected placement in the method depends on the writing method of the test code, so the error part of the test code can throw exceptions, leading to the wrong positive test. I didn't know that PMD, findbugs or sonar would provide tips for such code

@Test(expected = WantedException.class)
public void call2_should_throw_a_WantedException__not_call1() {
    // init tested
    tested.call1(); // may throw a WantedException

    // call to be actually tested
    tested.call2(); // the call that is supposed to raise an exception
}

>The expectedexception rule is also an attempt to fix the previous precautions, but it is a bit awkward to use it because it uses the expected style, which easymock users know very well Some may be convenient, but if you follow the principles of behavior driven development (BDD) or scheduled behavior assertion (AAA), the expectedexception rule will not be suitable for these writing styles In addition, it may be affected by the same problem as @ test, depending on what you expect

@Rule ExpectedException thrown = ExpectedException.none()

@Test
public void call2_should_throw_a_WantedException__not_call1() {
    // expectations
    thrown.expect(WantedException.class);
    thrown.expectMessage("boom");

    // init tested
    tested.call1(); // may throw a WantedException

    // call to be actually tested
    tested.call2(); // the call that is supposed to raise an exception
}

Even if the expected exception is placed before the test statement, if the test follows BDD or AAA, it will destroy your reading process

See also the author of the expectedexception on JUnit for this comment problem

Therefore, the above options have all their precautions, which obviously can not exempt the encoder from errors

>After creating this promising answer, I found a project, catch exception

As the project's description shows, it enables the encoder to write smooth lines of code, catch exceptions, and provide this exception for later assertion You can also use any assertion library, such as hamcrest or assertj

Quick examples from the home page:

// given: an empty list
List myList = new ArrayList();

// when: we try to get the first element of the list
when(myList).get(1);

// then: we expect an indexoutofboundsexception
then(caughtException())
        .isinstanceOf(indexoutofboundsexception.class)
        .hasMessage("Index: 1,Size: 0") 
        .hasNoCause();

As you can see, the code is really simple. You can catch exceptions on a specific line, and then the API is an alias. It will use the assert J API (similar to using assert that (Ex) hasNoCause()…)). In some cases, projects rely on the ancestors of Fest asset assertj Editor: it seems that the project is brewing Java 8 Lambdas support

At present, the library has two disadvantages:

>At the time of writing, it is worth noting that this library is based on mockito 1 x. Because it creates a simulation of the tested object behind the scene Since mockito has not been updated, the library cannot use the final class or final method Even if it is based on the current version of mockito 2, it will need to declare a global simulation manufacturer (inline manufacturer), which may not be what you want, because this simulator has different disadvantages, i.e. conventional simulator. > It requires another test dependency

Once the library supports Lambdas, these problems will not apply, but the assertj toolset will replicate these functions

Considering that if you don't want to use the catch exception tool, I would recommend the old good method of try catch block, at least to JDK7 For JDK 8 users, you may prefer to use assertj because it may not just assert exceptions. > Using JDK 8, lambs enter the test scenario, and they prove to be an interesting way to prove abnormal behavior Assertj has been updated to provide a good and smooth API to represent abnormal behavior

Sample tests for and assertj:

@Test
public void test_exception_approach_1() {
    ...
    assertThatExceptionOfType(IOException.class)
            .isThrownBy(() -> someBadioOperation())
            .withMessage("boom!"); 
}

@Test
public void test_exception_approach_2() {
    ...
    assertThatThrownBy(() -> someBadioOperation())
            .isinstanceOf(Exception.class)
            .hasMessageContaining("boom");
}

@Test
public void test_exception_approach_3() {
    ...
    // when
    Throwable thrown = catchThrowable(() -> someBadioOperation());

    // then
    assertThat(thrown).isinstanceOf(Exception.class)
                      .hasMessageContaining("boom");
}

>With JUnit 5 approaching full rewriting, assertions have been improved, and they may prove to be an out of the box way to prove valid exceptions However, the real assertion API is still a bit poor, and there is nothing other than assertthrows

@Test
@DisplayName("throws EmptyStackException when peeked")
void throwsExceptionWhenPeeked() {
    Throwable t = assertThrows(EmptyStackException.class,() -> stack.peek());

    Assertions.assertEquals("...",t.getMessage());
}

When you notice that assertequals still returns void, link assertions such as assertj are not allowed

In addition, if you remember a conflict with the name of matcher or asset, prepare the same conflict as assertions

I want to conclude that today (March 3, 2017) assertj's ease of use, discoverable API, fast development speed and de facto test dependency are the best solutions for jdk8, regardless of the test framework (JUnit or not). Therefore, the previous JDK should rely on try catch blocks, even if they feel cumbersome

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>