JUnit 테스트 주석으로 예외 메시지를 어떻게 확인합니까?


315

@Test주석이 있는 몇 가지 JUnit 테스트를 작성했습니다 . 테스트 메소드에서 확인 된 예외가 발생하고 예외와 함께 메시지를 표시하려는 경우 JUnit @Test주석 으로 처리 할 수있는 방법이 있습니까? AFAIK, JUnit 4.7은이 기능을 제공하지 않지만 이후 버전에서는이 기능을 제공합니까? .NET에서 메시지와 예외 클래스를 주장 할 수 있다는 것을 알고 있습니다. 자바 세계에서 비슷한 기능을 찾고 있습니다.

이것이 내가 원하는거야:

@Test (expected = RuntimeException.class, message = "Employee ID is null")
public void shouldThrowRuntimeExceptionWhenEmployeeIDisNull() {}

1
이제 조금 더 생각해 보았습니다 ... 메시지를 주장하는 것이 좋은 생각입니까? 귀하의 질문으로 인해 junit 소스 코드를 조금 파고 들었 으므로이 기능을 쉽게 추가 할 수 있었던 것 같습니다. 그들이 한 사실 되지는 날이 좋은 실례로 간주되지 않을 수 있습니다 생각합니다. 프로젝트에서 메시지를 주장하는 것이 왜 중요합니까?
c_maker

10
15 줄의 코드를 포함하는 메소드가 2 개의 다른 장소에서 동일한 예외를 throw한다고 가정 해보십시오. 내 테스트 사례는 예외 클래스뿐만 아니라 그 안에있는 메시지도 주장해야합니다. 이상적인 세계에서는 모든 비정상적인 동작에 자체 예외가 있어야합니다.이 경우 내 질문은 결코 발생하지 않지만 프로덕션 응용 프로그램에는 각 비정상적인 동작에 대한 고유 한 사용자 지정 예외가 없습니다.
Cshah

참고 @expectedExceptionMessage로 PHPUnit 에는 주석이 있습니다.
bancer

답변:


535

다음 @RuleExpectedException같이 주석을 사용할 수 있습니다 .

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

@Test
public void shouldThrowRuntimeExceptionWhenEmployeeIDisNull() throws Exception {
    expectedEx.expect(RuntimeException.class);
    expectedEx.expectMessage("Employee ID is null");

    // do something that should throw the exception...
    System.out.println("=======Starting Exception process=======");
    throw new NullPointerException("Employee ID is null");
}

ExpectedException문서 의 예제 는 (현재) 잘못되었습니다-공용 생성자가 없으므로을 사용해야 ExpectedException.none()합니다.


1
참고 : 나를 위해이 때 expectMessage빈 문자열로 지정된 메시지에 대한 비교는 수행되지 않았습니다
레드 데블

1
나에게 유용합니다. 감사. throws RuntimeException예외를 발생시키는 코드를 추가 한 후 testmethod에 있어야합니다 . 그것을 놓치지 마세요 ...
Bumbolt

5
작은 메소드 서브셋의 목적으로 필드를 작성하는 것은 나쁜 습관이기 때문에 개인적으로 이것을 사용하고 싶지 않습니다. 응답에 대한 비판이 아니라 JUnit의 디자인에 대한 비판. OP의 가상 솔루션은 존재한다면 훨씬 더 나을 것입니다.
Sridhar Sarnobat

2
@redDevil 다음 expectedMessage 검사 오류 메시지 (오류 메시지의 문자열 등)이 함수 지정 문자열 "포함"경우
tuan.dinh

3
문자열 매개 변수와 expectMessage는 예외 메시지를 사용 hamcrest의 정규의 일치를 들어, String.contains 검사를 수행failure.expectMessage(CoreMatchers.equalTo(...))
시바 발란

42

나는 @Rule대답을 좋아한다 . 그러나 어떤 이유로 규칙을 사용하지 않으려는 경우. 세 번째 옵션이 있습니다.

@Test (expected = RuntimeException.class)
public void myTestMethod()
{
   try
   {
      //Run exception throwing operation here
   }
   catch(RuntimeException re)
   {
      String message = "Employee ID is null";
      assertEquals(message, re.getMessage());
      throw re;
    }
    fail("Employee Id Null exception did not throw!");
  }

32

사용해야 @Test(expected=SomeException.class)합니까? 예외의 실제 메시지를 주장해야 할 때 이것이 바로 우리의 일입니다.

@Test
public void myTestMethod()
{
  try
  {
    final Integer employeeId = null;
    new Employee(employeeId);
    fail("Should have thrown SomeException but did not!");
  }
  catch( final SomeException e )
  {
    final String msg = "Employee ID is null";
    assertEquals(msg, e.getMessage());
  }
}

6
catch 블록을 작성하고 그 안에 assert를 사용하는 것을 알고 있지만 더 나은 코드 가독성을 위해 주석과 관련이 있습니다.
Cshah

또한 "올바른"방법으로 할 때와 같은 멋진 메시지를받지 못할 것입니다.
NeplatnyUdaj

15
JUnit이 제공 @Test(expected=...)하고 있는 try / catch 버전의 문제점 블록 의 끝에 호출을 잊어 버린ExpectedException 사람을 여러 번 본 적이 있다는 것 입니다. 코드 검토에 걸리지 않으면 테스트가 오탐 (false positive)이고 항상 통과하는 것입니다. fail()try
윌리엄 가격

이것이 내가이 선언적인 것들을 모두 좋아하지 않는 이유입니다. 원하는 것에 액세스하기가 어렵습니다.
Sridhar Sarnobat

30

JUnit 4.13에서는 다음을 수행 할 수 있습니다.

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;

...

@Test
void exceptionTesting() {
  IllegalArgumentException exception = assertThrows(
    IllegalArgumentException.class, 
    () -> { throw new IllegalArgumentException("a message"); }
  );

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

이것은 JUnit 5 에서도 작동 하지만 가져 오기가 다릅니다.

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

...

이 솔루션처럼. JUnit을 5로 이동해야
WesternGun

Gaaaaaaaaa. 4.13 현재 베타 버전이 아직 남아 있습니까 (2019 년 가을)? mvnrepository.com/artifact/junit/junit
granadaCoder

v4.13은 더 이상 베타 상태가 아닙니다 (2020 년 1 월 릴리스)
Simon

11

실제로 가장 좋은 사용법은 try / catch입니다. 왜? 예외가 예상되는 장소를 제어 할 수 있기 때문입니다.

이 예제를 고려하십시오.

@Test (expected = RuntimeException.class)
public void someTest() {
   // test preparation
   // actual test
}

언젠가 코드가 수정되고 테스트 준비에서 RuntimeException이 발생하면 어떻게됩니까? 이 경우 실제 테스트는 테스트되지 않으며 예외가 발생하지 않더라도 테스트는 통과합니다.

따라서 주석에 의존하는 것보다 try / catch를 사용하는 것이 훨씬 좋습니다.


슬프게도 이것도 저의 대답입니다.
Sridhar Sarnobat

2
코드 변경에 대한 우려는 작은 순열 관련 테스트 사례를 통해 완화됩니다. 때로는 불가피하며 catch / try 메소드에 의존해야하지만, 자주 발생하는 경우 테스트 케이스 함수를 작성하는 방식을 수정해야 할 가능성이 있습니다.
luis.espinal

테스트 및 / 또는 코드에 문제가 있습니다. 일반적인 RuntimeException을 기대하지 않거나 특정 예외 또는 최소한 특정 메시지를 예상합니다.
DennisK

내가 사용하는 RuntimeException다른 예외이 예외를 대체, 예를 들어.
Krzysztof Cislo

8

Raystorm은 좋은 대답을했습니다. 나도 규칙을 좋아하지 않습니다. 나는 가독성과 유용성을 돕기 위해 다음과 같은 유틸리티 클래스를 만드는 것을 제외하고는 비슷한 것을 수행한다. 이것은 처음에는 주석의 큰 장점 중 하나입니다.

이 유틸리티 클래스를 추가하십시오.

import org.junit.Assert;

public abstract class ExpectedRuntimeExceptionAsserter {

    private String expectedExceptionMessage;

    public ExpectedRuntimeExceptionAsserter(String expectedExceptionMessage) {
        this.expectedExceptionMessage = expectedExceptionMessage;
    }

    public final void run(){
        try{
            expectException();
            Assert.fail(String.format("Expected a RuntimeException '%s'", expectedExceptionMessage));
        } catch (RuntimeException e){
            Assert.assertEquals("RuntimeException caught, but unexpected message", expectedExceptionMessage, e.getMessage());
        }
    }

    protected abstract void expectException();

}

그런 다음 단위 테스트를 위해 필요한 것은 다음 코드입니다.

@Test
public void verifyAnonymousUserCantAccessPrivilegedResourceTest(){
    new ExpectedRuntimeExceptionAsserter("anonymous user can't access privileged resource"){
        @Override
        protected void expectException() {
            throw new RuntimeException("anonymous user can't access privileged resource");
        }
    }.run(); //passes test; expected exception is caught, and this @Test returns normally as "Passed"
}

2

@Rule을 사용하는 경우 예외 세트는 Test 클래스의 모든 테스트 메소드에 적용됩니다.


2
Jesse Merriman 응답을 사용하면 expectEx.expect () 및 expectedEx.expectMessage ()를 호출하는 테스트 메소드에서만 예외가 점검됩니다. 다른 메소드는 expectEx = ExpectedException.none () 정의를 사용합니다. 즉, 예외가 예상되지 않습니다.
Egl

2

Junit에서 예외를 주장하는 방법을 좋아하지 않았습니다. 주석에 "expected"를 사용하면 내 관점에서 "then"이 테스트 정의의 맨 위에 위치하기 때문에 "given, when, then"패턴을 위반하는 것 같습니다.

또한 "@Rule"을 사용하면 많은 상용구 코드를 처리해야합니다. 따라서 테스트를 위해 새 라이브러리를 설치할 수 있다면 AssertJ를 살펴보십시오 (이 라이브러리에는 SpringBoot가 제공됨)

그런 다음 "주어진 / 언제 / 다음"원칙을 위반하지 않는 테스트이며 AssertJ를 사용하여 다음을 확인합니다.

1-예외는 우리가 기대하는 것입니다. 2-또한 예상되는 메시지가 있습니다

다음과 같이 보일 것입니다 :

 @Test
void should_throwIllegalUse_when_idNotGiven() {

    //when
    final Throwable raisedException = catchThrowable(() -> getUserDAO.byId(null));

    //then
    assertThat(raisedException).isInstanceOf(IllegalArgumentException.class)
            .hasMessageContaining("Id to fetch is mandatory");
}

1

나는 user64141의 답변을 좋아하지만 더 일반화 될 수 있음을 발견했습니다. 여기 내 테이크가 있습니다 :

public abstract class ExpectedThrowableAsserter implements Runnable {

    private final Class<? extends Throwable> throwableClass;
    private final String expectedExceptionMessage;

    protected ExpectedThrowableAsserter(Class<? extends Throwable> throwableClass, String expectedExceptionMessage) {
        this.throwableClass = throwableClass;
        this.expectedExceptionMessage = expectedExceptionMessage;
    }

    public final void run() {
        try {
            expectException();
        } catch (Throwable e) {
            assertTrue(String.format("Caught unexpected %s", e.getClass().getSimpleName()), throwableClass.isInstance(e));
            assertEquals(String.format("%s caught, but unexpected message", throwableClass.getSimpleName()), expectedExceptionMessage, e.getMessage());
            return;
        }
        fail(String.format("Expected %s, but no exception was thrown.", throwableClass.getSimpleName()));
    }

    protected abstract void expectException();

}

try 블록 내에 "fail"문을 남겨두면 관련 어설 션 예외가 발생합니다. catch 문 내에서 return을 사용하면이를 방지 할 수 있습니다.


0

catch-exception 라이브러리를 가져 와서 사용하십시오. ExpectedException규칙 보다 훨씬 깨끗 합니다.try-catch .

문서 양식 예 :

import static com.googlecode.catchexception.CatchException.*;
import static com.googlecode.catchexception.apis.CatchExceptionHamcrestMatchers.*;

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

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

// then: we expect an IndexOutOfBoundsException with message "Index: 1, Size: 0"
assertThat(caughtException(),
  allOf(
    instanceOf(IndexOutOfBoundsException.class),
    hasMessage("Index: 1, Size: 0"),
    hasNoCause()
  )
);

-2
@Test (expectedExceptions = ValidationException.class, expectedExceptionsMessageRegExp = "This is not allowed")
public void testInvalidValidation() throws Exception{
     //test code
}


질문은 요구 Junit하지만 당신의 대답은주고 있습니다TestNG
Huazhe Yin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.