java 확인 JUnit 4 테스트에서 특정 예외가 발생했다고 어떻게 주장합니까?




throw exception test (24)

어떻게 JUnit4를 관용적으로 사용하여 일부 코드가 예외를 throw하는지 테스트 할 수 있습니까?

나는 확실히 이런 일을 할 수 있지만 :

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  boolean thrown = false;

  try {
    foo.doStuff();
  } catch (IndexOutOfBoundsException e) {
    thrown = true;
  }

  assertTrue(thrown);
}

주석이나 Assert.xyz가 있거나 이러한 종류의 상황에 대해 JUnit의 훨씬 덜 불쾌하고 정신이 훨씬 더 깊은 것을 기억합니다.


다음과 같이 끄고 켤 수있는 Matcher를 만드십시오.

public class ExceptionMatcher extends BaseMatcher<Throwable> {
    private boolean active = true;
    private Class<? extends Throwable> throwable;

    public ExceptionMatcher(Class<? extends Throwable> throwable) {
        this.throwable = throwable;
    }

    public void on() {
        this.active = true;
    }

    public void off() {
        this.active = false;
    }

    @Override
    public boolean matches(Object object) {
        return active && throwable.isAssignableFrom(object.getClass());
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("not the covered exception type");
    }
}

그것을 사용하려면 :

더하다 public ExpectedException exception = ExpectedException.none(); , 다음 :

ExceptionMatcher exMatch = new ExceptionMatcher(MyException.class);
exception.expect(exMatch);
someObject.somethingThatThrowsMyException();
exMatch.off();

junit에는 예외를 테스트하는 네 가지 방법이 있습니다.

  • junit4.x의 경우 Test annonation의 선택적 'expected'속성을 사용하십시오.

    @Test(expected = IndexOutOfBoundsException.class)
    public void testFooThrowsIndexOutOfBoundsException() {
        foo.doStuff();
    }
    
  • junit4.x의 경우 ExpectedException 규칙을 사용하십시오.

    public class XxxTest {
        @Rule
        public ExpectedException thrown = ExpectedException.none();
    
        @Test
        public void testFooThrowsIndexOutOfBoundsException() {
            thrown.expect(IndexOutOfBoundsException.class)
            //you can test the exception message like
            thrown.expectMessage("expected messages");
            foo.doStuff();
        }
    }
    
  • 또한 junit 3 프레임 워크에서 널리 사용되는 고전적인 try / catch 방식을 사용할 수 있습니다.

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        try {
            foo.doStuff();
            fail("expected exception was not occured.");
        } catch(IndexOutOfBoundsException e) {
            //if execution reaches here, 
            //it indicates this exception was occured.
            //so we need not handle it.
        }
    }
    
  • 마지막으로, junit5.x에서 다음과 같이 assertThrows를 사용할 수 있습니다.

    @Test
    public void testFooThrowsIndexOutOfBoundsException() {
        Throwable exception = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff());
        assertEquals("expected messages", exception.getMessage());
    }
    
  • 그래서

    • 첫 번째 방법은 예외 유형 만 테스트 할 때 사용됩니다.
    • 테스트 예외 메시지를 추가로 원할 때 다른 세 가지 방법이 사용됩니다.
    • junit 3을 사용하면 세 번째 것이 더 좋습니다.
    • junit 5가 마음에 들면, 4 번째를 좋아해야합니다.
  • 자세한 정보는 github.com/junit-team/junit/wiki/Exception-testingjunit5 사용자 안내서 를 참조하십시오.


예상 된 예외를 사용하면 테스트에서 특정 코드 행이 아닌 해당 예외가 발생했다고 주장하기 때문에 조심하십시오.

이러한 방법은 일반적으로 매우 간단하기 때문에 매개 변수 유효성 검사를 테스트하는 데이 방법을 사용하는 경향이 있지만 더 복잡한 테스트를 더 잘 수행 할 수 있습니다.

try {
    methodThatShouldThrow();
    fail( "My method didn't throw when I expected it to" );
} catch (MyException expectedException) {
}

판단을 적용하십시오.


이전에 대답했듯이 JUnit에서 예외를 처리하는 방법은 다양합니다. 그러나 Java 8에는 또 다른 것이 있습니다 : 람다 식을 사용합니다. Lambda Expressions를 사용하면 다음과 같은 구문을 얻을 수 있습니다.

@Test
public void verifiesTypeAndMessage() {
    assertThrown(new DummyService()::someMethod)
            .isInstanceOf(RuntimeException.class)
            .hasMessage("Runtime exception occurred")
            .hasMessageStartingWith("Runtime")
            .hasMessageEndingWith("occurred")
            .hasMessageContaining("exception")
            .hasNoCause();
}

assertThrown은 람다 식, 메서드 참조 또는 생성자 참조로 인스턴스를 만들 수있는 함수 인터페이스를 허용합니다. 그 인터페이스를 받아들이는 assertThrown는 예외를 처리 할 준비가되어있을 것이다.

이것은 비교적 간단하면서도 강력한 기술입니다.

이 기술을 설명하는이 블로그 게시물을 살펴보십시오. http://blog.codeleak.pl/2014/07/junit-testing-exception-with-java-8-and-lambda-expressions.html

소스 코드는 https://github.com/kolorobot/unit-testing-demo/tree/master/src/test/java/com/github/kolorobot/exceptions/java8 에서 찾을 수 있습니다.

공개 : 저는 블로그와 프로젝트의 저자입니다.


IMHO, JUnit에서 예외를 확인하는 가장 좋은 방법은 try / catch / fail / assert 패턴입니다.

// this try block should be as small as possible,
// as you want to make sure you only catch exceptions from your code
try {
    sut.doThing();
    fail(); // fail if this does not throw any exception
} catch(MyException e) { // only catch the exception you expect,
                         // otherwise you may catch an exception for a dependency unexpectedly
    // a strong assertion on the message, 
    // in case the exception comes from anywhere an unexpected line of code,
    // especially important if your checking IllegalArgumentExceptions
    assertEquals("the message I get", e.getMessage()); 
}

assertTrue 는 일부 사람들에게는 약간 assertTrue 수 있으므로 assertThat(e.getMessage(), containsString("the message"); 이 바람직 할 수 있습니다.


Java8을 사용하는 Junit4 솔루션은이 기능을 사용합니다.

public Throwable assertThrows(Class<? extends Throwable> expectedException, java.util.concurrent.Callable<?> funky) {
    try {
        funky.call();
    } catch (Throwable e) {
        if (expectedException.isInstance(e)) {
            return e;
        }
        throw new AssertionError(
                String.format("Expected [%s] to be thrown, but was [%s]", expectedException, e));
    }
    throw new AssertionError(
            String.format("Expected [%s] to be thrown, but nothing was thrown.", expectedException));
}

사용법은 다음과 같습니다.

    assertThrows(ValidationException.class,
            () -> finalObject.checkSomething(null));

유일한 제한은 final람다 식에서 객체 참조 를 사용하는 것 입니다. 이 솔루션은 솔루션을 사용하여 메서드 수준에서 테스트 할 수있는 대신 테스트 어설 션을 계속할 수 있습니다 @Test(expected = IndexOutOfBoundsException.class).


같은 문제를 해결하기 위해 작은 프로젝트를 설정했습니다. http://code.google.com/p/catch-exception/

이 작은 조력자를 쓰면 쓸 것입니다.

verifyException(foo, IndexOutOfBoundsException.class).doStuff();

이것은 JUnit 4.7의 ExpectedException 규칙보다 간략합니다. skaffman에서 제공하는 솔루션과 비교하여 예외를 예상하는 코드 행을 지정할 수 있습니다. 이게 도움이 되길 바란다.


자바 8 람다를 사용하는 나의 해결책 :

public static <T extends Throwable> T assertThrows(Class<T> expected, ThrowingRunnable action) throws Throwable {
    try {
        action.run();
        Assert.fail("Did not throw expected " + expected.getSimpleName());
        return null; // never actually
    } catch (Throwable actual) {
        if (!expected.isAssignableFrom(actual.getClass())) { // runtime '!(actual instanceof expected)'
            System.err.println("Threw " + actual.getClass().getSimpleName() 
                               + ", which is not a subtype of expected " 
                               + expected.getSimpleName());
            throw actual; // throw the unexpected Throwable for maximum transparency
        } else {
            return (T) actual; // return the expected Throwable for further examination
        }
    }
}

FunctionalInterface Runnable는 필수 항목을 선언하지 않으므로 정의 해야합니다 throws.

@FunctionalInterface
public interface ThrowingRunnable {
    void run() throws Throwable;
}

이 방법은 다음과 같이 사용할 수 있습니다.

class CustomException extends Exception {
    public final String message;
    public CustomException(final String message) { this.message = message;}
}
CustomException e = assertThrows(CustomException.class, () -> {
    throw new CustomException("Lorem Ipsum");
});
assertEquals("Lorem Ipsum", e.message);

Java 8을 사용하면 검사 할 코드와 예상되는 예외를 매개 변수로 취하는 메소드를 작성할 수 있습니다.

private void expectException(Runnable r, Class<?> clazz) { 
    try {
      r.run();
      fail("Expected: " + clazz.getSimpleName() + " but not thrown");
    } catch (Exception e) {
      if (!clazz.isInstance(e)) fail("Expected: " + clazz.getSimpleName() + " but " + e.getClass().getSimpleName() + " found", e);
    }
  }

그런 다음 테스트를 수행합니다.

expectException(() -> list.sublist(0, 2).get(2), IndexOutOfBoundsException.class);

은혜:

  • 어떤 도서관에도 의존하지 않는다.
  • 로컬 리 제이션 된 체크 - 좀 더 정확하고 필요한 경우 하나의 테스트 내에서 이와 같은 다중 어설 션을 허용합니다.
  • 사용하기 쉬운

JUnit과 함께 사용할 수있는 AssertJ 정문 사용 :

import static org.assertj.core.api.Assertions.*;

@Test
public void testFooThrowsIndexOutOfBoundsException() {
  Foo foo = new Foo();

  assertThatThrownBy(() -> foo.doStuff())
        .isInstanceOf(IndexOutOfBoundsException.class);
}

그것은 테스트에서 예상되는 라인이 예외를 던 @Test(expected=IndexOutOfBoundsException.class) 을 보장하고 message와 같은 예외에 대한 더 자세한 정보를 확인할 수 있기 때문에 @Test(expected=IndexOutOfBoundsException.class) 보다 좋습니다.

assertThatThrownBy(() ->
       {
         throw new Exception("boom!");
       })
    .isInstanceOf(Exception.class)
    .hasMessageContaining("boom");

Maven / Gradle 지침은 여기에 있습니다.


Java 8 솔루션

다음과 같은 솔루션을 원한다면 :

  • Java 8 lambdas 활용
  • JUnit 마법에 의존 하지 않습니다.
  • 단일 테스트 방법 내에서 여러 예외를 확인할 수 있습니다.
  • 전체 테스트 메소드에서 알 수없는 라인 대신 테스트 메소드 내의 특정 라인 세트에 의해 예외가 던져 지는지 점검합니다.
  • 더 이상 검사 할 수 있도록 throw 된 실제 예외 객체를 나타냅니다.

다음은 내가 작성한 유틸리티 함수입니다.

public final <T extends Throwable> T expectException( Class<T> exceptionClass, Runnable runnable )
{
    try
    {
        runnable.run();
    }
    catch( Throwable throwable )
    {
        if( throwable instanceof AssertionError && throwable.getCause() != null )
            throwable = throwable.getCause(); //allows "assert x != null : new IllegalArgumentException();"
        assert exceptionClass.isInstance( throwable ) : throwable; //exception of the wrong kind was thrown.
        assert throwable.getClass() == exceptionClass : throwable; //exception thrown was a subclass, but not the exact class, expected.
        @SuppressWarnings( "unchecked" )
        T result = (T)throwable;
        return result;
    }
    assert false; //expected exception was not thrown.
    return null; //to keep the compiler happy.
}

( 내 블로그에서 가져온 )

다음과 같이 사용하십시오.

@Test
public void testThrows()
{
    RuntimeException e = expectException( RuntimeException.class, () -> 
        {
            throw new RuntimeException( "fail!" );
        } );
    assert e.getMessage().equals( "fail!" );
}

예를 들어, 아래에 언급 된 코드 조각에 대해 Junit을 작성하려고합니다.

public int divideByZeroDemo(int a,int b){

    return a/b;
}

public void exceptionWithMessage(String [] arr){

    throw new ArrayIndexOutOfBoundsException("Array is out of bound");
}

위 코드는 발생할 수있는 알 수없는 예외를 테스트하는 것이며, 아래 코드는 사용자 지정 메시지에 대해 일부 예외를 선언하는 것입니다.

 @Rule
public ExpectedException exception=ExpectedException.none();

private Demo demo;
@Before
public void setup(){

    demo=new Demo();
}
@Test(expected=ArithmeticException.class)
public void testIfItThrowsAnyException() {

    demo.divideByZeroDemo(5, 0);

}

@Test
public void testExceptionWithMessage(){


    exception.expectMessage("Array is out of bound");
    exception.expect(ArrayIndexOutOfBoundsException.class);
    demo.exceptionWithMessage(new String[]{"This","is","a","demo"});
}

JUnit 5 솔루션

@Test
void testFooThrowsIndexOutOfBoundsException() {    
  Throwable exception = expectThrows( IndexOutOfBoundsException.class, foo::doStuff );

  assertEquals( "some message", exception.getMessage() );
}

JUnit 5에 대한 자세한 정보는 improved


JUnit 4 이상에서는 다음과 같이 예외를 테스트 할 수 있습니다.

@Rule
public ExpectedException exceptions = ExpectedException.none();


JUnit 테스트를 개선하는 데 사용할 수있는 많은 기능을 제공합니다.
아래 예제를 보면 예외적으로 세 가지를 테스트하고 있습니다.

  1. Throw 된 예외의 형태
  2. 예외 메시지
  3. 예외의 원인


public class MyTest {

    @Rule
    public ExpectedException exceptions = ExpectedException.none();

    ClassUnderTest classUnderTest;

    @Before
    public void setUp() throws Exception {
        classUnderTest = new ClassUnderTest();
    }

    @Test
    public void testAppleisSweetAndRed() throws Exception {

        exceptions.expect(Exception.class);
        exceptions.expectMessage("this is the exception message");
        exceptions.expectCause(Matchers.<Throwable>equalTo(exceptionCause));

        classUnderTest.methodUnderTest("param1", "param2");
    }

}

JUnit 5가 발표되었으므로, 가장 좋은 방법은 Assertions.assertThrows() 를 사용하는 것입니다 ( Junit 5 사용자 안내서 참조 ).

다음은 예외가 발생했는지 확인하는 예제입니다. 예외 메시지에 대한 Truth 를 만들기 위해 Truth 을 사용합니다.

public class FooTest {
  @Test
  public void doStuffThrowsIndexOutOfBoundsException() {
    Foo foo = new Foo();

    IndexOutOfBoundsException e = assertThrows(
        IndexOutOfBoundsException.class, foo::doStuff);

    assertThat(e).hasMessageThat().contains("woops!");
  }
}

다른 답변의 접근 방식에 비해 장점은 다음과 같습니다.

  1. JUnit에 내장
  2. 람다의 코드가 예외를 발생시키지 않으면 유용한 예외 메시지를 얻고 다른 예외를 throw하면 스택 추적을 얻습니다.
  3. 간결한
  4. 테스트가 Arrange-Act-Assert를 따르도록 허용합니다.
  5. 예외를 throw하려는 코드를 정확하게 나타낼 수 있습니다.
  6. throws 절에 예상되는 예외를 나열 할 필요가 없습니다.
  7. 캐치 된 예외에 대한 어설 션을 만들기 위해 선택한 어설 션 프레임 워크를 사용할 수 있습니다

유사한 메소드가 JUnit 4.13의 org.junit Assert 에 추가됩니다.


JUnit 4는 다음을 지원합니다.

@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {
    ArrayList emptyList = new ArrayList();
    Object o = emptyList.get(0);
}

참조 : https://junit.org/junit4/faq.html#atests_7


가 말한 것에 을 확인하십시오 :

  • ExpectedException 인스턴스가 public ( 관련 질문 )
  • ExpectedException @Before 메서드에서 인스턴스화 되지 않습니다 . 이 post 은 JUnit의 실행 명령의 모든 복잡함을 명확하게 설명합니다.

이것을 하지 마십시오 :

@Rule    
public ExpectedException expectedException;

@Before
public void setup()
{
    expectedException = ExpectedException.none();
}

마지막 this 블로그 게시물은 특정 예외가 발생했다고 주장하는 방법을 명확하게 보여줍니다.


다음과 같이 할 수도 있습니다.

@Test
public void testFooThrowsIndexOutOfBoundsException() {
    try {
        foo.doStuff();
        assert false;
    } catch (IndexOutOfBoundsException e) {
        assert true;
    }
}

어때요? 아주 일반적인 예외를 잡아서 catch 블록 밖으로 빼내고 예외 클래스가 예상 한 것이라고 주장하십시오. 이 어설 션은 a) 예외가 잘못된 유형 (예 : 널 포인터가있는 경우) 및 b) 예외가 발생하지 않은 경우 실패합니다.

public void testFooThrowsIndexOutOfBoundsException() {
  Throwable e = null;

  try {
    foo.doStuff();
  } catch (Throwable ex) {
    e = ex;
  }

  assertTrue(e instanceof IndexOutOfBoundsException);
}

제 경우에는 항상 db로부터 RuntimeException을 얻지 만 메시지는 다릅니다. 예외는 각각 처리해야합니다. 다음은 내가 테스트 한 방법입니다.

@Test
public void testThrowsExceptionWhenWrongSku() {

    // Given
    String articleSimpleSku = "999-999";
    int amountOfTransactions = 1;
    Exception exception = null;

    // When
    try {
        createNInboundTransactionsForSku(amountOfTransactions, articleSimpleSku);
    } catch (RuntimeException e) {
        exception = e;
    }

    // Then
    shouldValidateThrowsExceptionWithMessage(exception, MESSAGE_NON_EXISTENT_SKU);
}

private void shouldValidateThrowsExceptionWithMessage(final Exception e, final String message) {
    assertNotNull(e);
    assertTrue(e.getMessage().contains(message));
}

예외를 리턴해야하는 메소드 다음에 어설 션 실패를 사용할 수 있습니다.

try{
   methodThatThrowMyException();
   Assert.fail("MyException is not thrown !");
} catch (final Exception exception) {
   // Verify if the thrown exception is instance of MyException, otherwise throws an assert failure
   assertTrue(exception instanceof MyException, "An exception other than MyException is thrown !");
   // In case of verifying the error message
   MyException myException = (MyException) exception;
   assertEquals("EXPECTED ERROR MESSAGE", myException.getMessage());
}

여기에서 많은 방법을 시도했지만 복잡했거나 제 요구 사항을 제대로 충족하지 못했습니다. 사실, 도우미 메서드를 매우 간단하게 작성할 수 있습니다.

public class ExceptionAssertions {
    public static void assertException(BlastContainer blastContainer ) {
        boolean caughtException = false;
        try {
            blastContainer.test();
        } catch( Exception e ) {
            caughtException = true;
        }
        if( !caughtException ) {
            throw new AssertionFailedError("exception expected to be thrown, but was not");
        }
    }
    public static interface BlastContainer {
        public void test() throws Exception;
    }
}

다음과 같이 사용하십시오.

assertException(new BlastContainer() {
    @Override
    public void test() throws Exception {
        doSomethingThatShouldExceptHere();
    }
});

0 종족 : mockito 필요 없음, powermock 필요 없음; 최종 수업에서 잘 작동합니다.


assertj-corejunit 테스트에서 예외 처리 라이브러리 권장

자바 8, 이런 :

//given

//when
Throwable throwable = catchThrowable(() -> anyService.anyMethod(object));

//then
AnyException anyException = (AnyException) throwable;
assertThat(anyException.getMessage()).isEqualTo("........");
assertThat(exception.getCode()).isEqualTo(".......);

테스트 케이스 작성에는 두 가지 방법이 있습니다.

  1. 메소드에 의해 던져지는 예외로 주석을 달아라. 이 같은@Test(expected = IndexOutOfBoundsException.class)
  2. try catch 블록을 사용하여 테스트 클래스에서 예외를 catch하고 테스트 클래스의 메서드에서 throw 된 메시지에 대해 단정 할 수 있습니다.

    try{
    }
    catch(exception to be thrown from method e)
    {
         assertEquals("message", e.getmessage());
    }
    

이 질문에 행복하게 답변 해 주시기 바랍니다.





assert