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

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

@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.