JUnit4를 관용적으로 사용하여 일부 코드에서 예외가 발생하는지 테스트하려면 어떻게해야합니까?
나는 확실히 이와 같은 것을 할 수 있지만 :
@Test
public void testFooThrowsIndexOutOfBoundsException() {
boolean thrown = false;
try {
foo.doStuff();
} catch (IndexOutOfBoundsException e) {
thrown = true;
}
assertTrue(thrown);
}
이러한 종류의 상황에 대해 주석이나 Assert.xyz 또는 JUnit의 훨씬 덜 혼란스럽고 훨씬 더 정신적 인 것이 있다는 것을 기억합니다 .
답변
JUnit 버전과 사용하는 어설 션 라이브러리에 따라 다릅니다.
- JUnit5 및 4.13의 경우 답변 https://stackoverflow.com/a/2935935/2986984
- assertJ 또는 google-truth를 사용하는 경우 답변 https://stackoverflow.com/a/41019785/2986984
원래 답변 JUnit <= 4.12
은 다음과 같습니다.
@Test(expected = IndexOutOfBoundsException.class)
public void testIndexOutOfBoundsException() {
ArrayList emptyList = new ArrayList();
Object o = emptyList.get(0);
}
답변 https://stackoverflow.com/a/31826781/2986984 에는 JUnit <= 4.12에 대한 추가 옵션이 있습니다.
참고 :
답변
편집 : 이제 JUnit 5 및 JUnit 4.13이 릴리스되었으므로 가장 좋은 옵션은 Assertions.assertThrows()
(JUnit 5의 경우) 및 Assert.assertThrows()
(JUnit 4.13의 경우)를 사용하는 것입니다. 자세한 내용은 다른 답변 을 참조하십시오.
JUnit 5로 마이그레이션하지 않았지만 JUnit 4.7을 사용할 수있는 경우 ExpectedException
규칙을 사용할 수 있습니다 .
public class FooTest {
@Rule
public final ExpectedException exception = ExpectedException.none();
@Test
public void doStuffThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();
exception.expect(IndexOutOfBoundsException.class);
foo.doStuff();
}
}
전에 던져 @Test(expected=IndexOutOfBoundsException.class)
지면 테스트가 실패하기 때문에 이것은 훨씬 낫습니다.IndexOutOfBoundsException
foo.doStuff()
자세한 내용은 이 기사 를 참조하십시오
답변
메소드 가 테스트에서 특정 코드 줄이 아니라 해당 예외를 발생 시켰다고 주장하기 때문에 예상되는 예외를 사용하는 데주의하십시오 .
이러한 방법은 일반적으로 매우 간단하지만 더 복잡한 테스트가 더 잘 제공 될 수 있기 때문에 매개 변수 유효성 검사 테스트에 사용하는 경향이 있습니다.
try {
methodThatShouldThrow();
fail( "My method didn't throw when I expected it to" );
} catch (MyException expectedException) {
}
판단을 적용하십시오.
답변
이전에 대답했듯이 JUnit에는 예외를 처리하는 여러 가지 방법이 있습니다. 그러나 Java 8에는 Lambda Expressions 사용이 있습니다. 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 에서 찾을 수 있습니다.
공개 : 저는 블로그와 프로젝트의 저자입니다.
답변
junit에는 예외를 테스트하는 네 가지 방법이 있습니다.
junit5.x
-
junit5.x의 경우
assertThrows
다음과 같이 사용할 수 있습니다@Test public void testFooThrowsIndexOutOfBoundsException() { Throwable exception = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff()); assertEquals("expected messages", exception.getMessage()); }
junit4.x
-
junit4.x의 경우, 선택 테스트 테스트의 ‘예상’속성을 사용하십시오.
@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. } }
-
그래서
- junit 5를 좋아한다면 첫 번째 것을 좋아해야합니다
- 두 번째 방법은 예외 유형 만 테스트하려는 경우에 사용됩니다.
- 첫 번째와 마지막 두 개는 테스트 예외 메시지를 추가로 원할 때 사용됩니다.
- junit 3을 사용하면 네 번째 것이 선호됩니다.
-
자세한 내용은 이 문서 와 자세한 내용은 junit5 사용 설명서 를 참조하십시오.
답변
tl; dr
-
post-JDK8 : AssertJ 또는 커스텀 람다를 사용 하여 뛰어난 동작을 확인하십시오.
-
pre-JDK8 : 오래된 good
try
–catch
block을 추천합니다 . ( 블록 앞에 어설 션 을 추가하는 것을 잊지 마십시오fail()
catch
)
Junit 4 또는 JUnit 5에 상관없이
긴 이야기
자신을 작성할 수 있습니다 그것을 스스로 할 try
– catch
블록 또는 JUnit을 도구 (사용 @Test(expected = ...)
또는 @Rule ExpectedException
JUnit을 규칙 기능).
그러나 이러한 방법은 그다지 우아하지 않으며 다른 도구와 잘 가독성 이 좋지 않습니다 . 또한 JUnit 툴링에는 몇 가지 함정이 있습니다.
-
try
–catch
당신이 테스트 문제를 해결 블록을 작성하고 catch 블록의 주장을 작성해야 블록, 즉 미세하지만 많은 발견 할 수있다이 스타일은 인터럽트 테스트의 읽기 흐름을 그. 또한, 당신은 쓸 필요Assert.fail
의 끝 부분에try
블록. 그렇지 않으면 테스트에서 어설 션의 한쪽이 누락 될 수 있습니다. PMD , findbugs 또는 Sonar 는 이러한 문제를 발견합니다. -
@Test(expected = ...)
적은 코드를 작성하고이 테스트를 작성하는 것은 오류를 코딩 가정으로 적은 경향 수있는 기능은 흥미 롭다. 그러나 일부 지역에서는 이러한 접근 방식이 부족합니다.- 테스트에서 원인 또는 메시지와 같은 예외에 대한 추가 사항을 확인해야하는 경우 (예외 메시지가 중요하면 정확한 예외 유형이 충분하지 않을 수 있음)
-
또한 테스트 코드 작성 방법에 따라 테스트 코드의 잘못된 부분에서 예외가 발생하여 거짓 양성 테스트가 발생할 수 있으며 PMD , findbugs 또는 Sonar 확신하지 못합니다 그러한 코드에 대한 힌트를 줄 것입니다.
@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 }
-
이
ExpectedException
규칙은 또한 이전 경고를 해결하려는 시도이지만, 예상 스타일을 사용하기 때문에 사용하기가 약간 어색하다고 생각합니다. EasyMock 사용자는이 스타일을 매우 잘 알고 있습니다. 일부에게는 편리 할 수 있지만 BDD ( behavior Driven Development ) 또는 AAA ( Action Actassert ) 원칙을 따르는 경우ExpectedException
규칙이 해당 작성 스타일에 맞지 않습니다. 그 외에도@Test
예상 위치에 따라 방법 과 동일한 문제가 발생할 수 있습니다 .@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 }
예상되는 예외도 테스트 문 앞에 놓여지며 테스트가 BDD 또는 AAA를 따르는 경우 읽기 흐름이 중단됩니다.
또한 의 작성자 JUnit에 대한 이 주석 문제를 참조하십시오
ExpectedException
. JUnit 4.13-beta-2 는이 메커니즘을 더 이상 사용하지 않습니다.풀 요청 # 1519 : Deprecate ExpectedException
Assert.assertThrows 메소드는 예외를 확인하는 더 좋은 방법을 제공합니다. 또한 규칙의 순서가 중요하기 때문에 TestWatcher와 같은 다른 규칙과 함께 사용하면 ExpectedException을 사용하는 것이 오류가 발생하기 쉽습니다.
따라서 위의 옵션에는 모든주의 사항이 있으며 코더 오류에 영향을 미치지 않습니다.
-
유망한 것으로 보이는이 답변을 만든 후에 내가 알게 된 프로젝트가 있습니다. 그것은 예외 입니다.
프로젝트에 대한 설명에서 알 수 있듯이 코더는 유창한 코드 줄을 작성하여 예외를 포착하고 후자의 주장에 대해이 예외를 제공 할 수 있습니다. 그리고 Hamcrest 또는 AssertJ 와 같은 어설 션 라이브러리를 사용할 수 있습니다 .
홈페이지에서 가져온 빠른 예 :
// 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();
코드가 정말 간단
then
하다는 것을 알 수 있듯이 특정 라인에서 예외를 포착하면 API는 AssertJ API를 사용하는 별칭입니다 (과 유사assertThat(ex).hasNoCause()...
). 어느 시점에서 프로젝트는 AssertJ의 조상 FEST-Assert에 의존했습니다 . 편집 : 프로젝트가 Java 8 Lambdas 지원을 양조하고있는 것 같습니다.현재이 라이브러리에는 두 가지 단점이 있습니다.
-
이 글을 쓰는 시점에서이 라이브러리는 Mockito 1.x를 기반으로하며, 테스트 된 객체 뒤에 장면을 모방 한 것입니다. Mockito가 여전히 업데이트되지 않았으므로이 라이브러리는 최종 클래스 또는 최종 메소드와 함께 작동 할 수 없습니다 . 그리고 현재 버전의 Mockito 2를 기반으로 했더라도 글로벌 모의 메이커 (
inline-mock-maker
) 를 선언해야합니다. 이 모의 메이커는 일반 모의 메이커와는 다른 단점이 있기 때문에 원치 않을 수도 있습니다. -
또 다른 테스트 종속성이 필요합니다.
라이브러리가 람다를 지원하면 이러한 문제는 적용되지 않습니다. 그러나이 기능은 AssertJ 툴셋에 의해 복제됩니다.
catch-exception 도구를 사용하지 않으려면 모든 것을 고려 하여 적어도 JDK7까지
try
–catch
블록 의 오래된 좋은 방법을 권장합니다 . JDK 8 사용자의 경우 AssertJ를 사용하는 것이 좋습니다. 예외를 주장하는 것 이상을 제공 할 수도 있습니다. -
-
JDK8을 사용하면 람다는 테스트 현장에 들어가고 뛰어난 행동을 나타내는 흥미로운 방법으로 판명되었습니다. AssertJ는 유창한 유창한 API를 제공하여 예외적 인 행동을하도록 업데이트되었습니다.
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"); }
-
JUnit 5를 거의 완전히 다시 작성하면 어설 션이 약간 개선 되어 제대로 예외를 주장하는 즉시 사용 가능한 방법으로 흥미로울 수 있습니다. 그러나 실제로 어설 션 API는 여전히 약간 나쁘지만 외부에는 아무것도 없습니다
assertThrows
.@Test @DisplayName("throws EmptyStackException when peeked") void throwsExceptionWhenPeeked() { Throwable t = assertThrows(EmptyStackException.class, () -> stack.peek()); Assertions.assertEquals("...", t.getMessage()); }
알다시피
assertEquals
여전히을 반환void
하고 AssertJ와 같은 체인 어설 션을 허용하지 않습니다.당신이 이름 충돌을 기억한다면 또한
Matcher
또는Assert
와 같은 충돌을 만날 준비Assertions
.
오늘 (2017-03-03) AssertJ 의 사용 편의성, 검색 가능한 API, 빠른 개발 속도 및 사실상 테스트 종속성은 테스트 프레임 워크 (JUnit)에 관계없이 JDK8을 사용하는 가장 좋은 솔루션이라고 결론 내리고 싶습니다 여부), 이전의 JDK 대신에 의존한다 try
–catch
그들이 투박한 느낌이 경우에도 블록.
이 답변은 동일한 가시성을 가지고 있지 않은 다른 질문 에서 복사되었습니다 . 저는 같은 저자입니다.
답변
JUnit 5 및 JUnit 4.13이 릴리스되었으므로 가장 좋은 옵션은 Assertions.assertThrows()
(JUnit 5의 경우) 및 Assert.assertThrows()
(JUnit 4.13의 경우)를 사용하는 것입니다. Junit 5 사용자 안내서를 참조하십시오 .
다음은 예외가 발생했는지 확인하고 Truth 를 사용 하여 예외 메시지에 대한 어설 션을 만드는 예입니다 .
public class FooTest {
@Test
public void doStuffThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();
IndexOutOfBoundsException e = assertThrows(
IndexOutOfBoundsException.class, foo::doStuff);
assertThat(e).hasMessageThat().contains("woops!");
}
}
다른 답변의 접근법보다 장점은 다음과 같습니다.
- JUnit에 내장
- 람다의 코드에서 예외가 발생하지 않으면 유용한 예외 메시지가 표시되고 다른 예외가 발생하면 스택 추적이 표시됩니다.
- 간결한
- 테스트가 Arrange-Act-Assert를 따르도록 허용
- 예외를 던질 것으로 예상되는 코드를 정확하게 나타낼 수 있습니다
throws
절에 예상되는 예외를 나열 할 필요는 없습니다.- 선택한 어설 션 프레임 워크를 사용하여 포착 된 예외에 대한 어설 션을 만들 수 있습니다.
org.junit Assert
JUnit 4.13 에도 비슷한 방법이 추가 될 것 입니다.
