2010-03-18 7 views
191

Я написал несколько тестов JUnit с аннотацией @Test. Если мой тестовый метод выдает исключенное исключение и если я хочу утверждать это сообщение вместе с исключением, есть ли способ сделать это с помощью аннотации JUnit @Test? AFAIK, JUnit 4.7 не предоставляет эту функцию, но предоставляет ли ее какие-либо будущие версии? Я знаю, что в .NET вы можете утверждать сообщение и класс исключений. Ищите аналогичную функцию в мире Java.Как утвердить мое сообщение об исключении с аннотацией JUnit Test?

Это то, что я хочу:

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

Теперь, когда я думаю об этом немного больше ... Вы уверены, что это хорошая идея, чтобы утверждать это сообщение? Ваш вопрос заставлял меня копаться в исходном коде junit, и, похоже, они могли легко добавить эту функцию. Тот факт, что они делали * не *, заставляет меня думать, что это не может считаться хорошей практикой. Почему в вашем проекте важно утверждать это сообщение? –

+6

Хороший вопрос. Скажите, что метод, содержащий 15 строк кода, вызывает одно и то же исключение из двух разных мест. В моих тестовых случаях нужно утверждать не только класс исключения, но и сообщение в нем. В идеальном мире любое ненормальное поведение должно иметь свое собственное исключение. Если бы это было так, мой вопрос никогда не возникнет, но производственные приложения не имеют своего уникального пользовательского исключения для каждого ненормального поведения. – Cshah

ответ

333

Вы можете использовать @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... 
} 

Обратите внимание, что пример в ExpectedException Документах (в настоящее время) не так - там нет общественного конструктора, так что вы должны использовать ExpectedException.none().

+0

Примечание: для меня, когда 'expectMessage' был указан как пустая строка, сравнение для сообщения не было выполнено. – redDevil

+1

Полезно для меня. Благодарю. В тестовом методе должен быть 'throws RuntimeException' после добавления кода, который генерирует исключение. Не поймайте это ... – Bumbolt

+2

Я лично не хотел бы использовать это, так как создание полей с целью небольшого подмножества методов - это плохая практика. Не критика ответа, а дизайн Юнита. Гипотетическое решение ОП было бы намного лучше, если бы оно существовало. –

20

Вы должны использовать @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()); 
    } 
} 
+5

Я знаю, что нужно писать блок catch и использовать assert внутри этого, но для лучшей читаемости кода я хочу делать с аннотациями. – Cshah

+0

Также вы не получите такого приятного сообщения, как при правильном обращении. – NeplatnyUdaj

+8

Проблема с версией try/catch, теперь, когда JUnit предоставляет '@Test (expected = ...)' и 'ExpectedException', заключается в том, что я неоднократно видел, что кто-то хочет оставить вызов' fail() ' в конце блока 'try'_. Если вы не поймали проверку кода, ваш тест может быть ложноположительным и всегда проходить. –

2

При использовании @Rule набор исключений применяется ко всем методам тестирования в классе Test.

+2

Использование Джесси Мерриман ответ, исключение проверяется только в методах тестирования, которые вызывают ожидаемыеEx.expect() и expectedEx.expectMessage(). Другие методы будут использовать определение expectedEx = ExpectedException.none(), то есть исключение не ожидается. – Egl

20

Мне нравится @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!"); 
    } 
+6

Это уродливый af – Danon

+0

Не будет ли этот тест всегда терпеть неудачу? ошибка выходит за пределы блока catch. –

+2

@stolen_leaves Только в том случае, если код в блоке catch не генерирует исключение. – Raystorm

6

У 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" 
} 
4

На самом деле, лучшее использование является с Try/уловом. Зачем? Потому что вы можете контролировать место, где вы ожидаете исключения.

Рассмотрим следующий пример:

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

Что делать, если один день код модифицируется и тестируемый препарат будет бросить RuntimeException? В этом случае фактический тест даже не проверяется, и даже если он не станет исключением, тест пройдет.

Вот почему гораздо лучше использовать try/catch, чем полагаться на аннотацию.

+0

К сожалению, это тоже мой ответ. –

+1

Проблемы, связанные с изменением кода, устраняются с помощью небольших тестовых примеров с перестановкой. Иногда это неизбежно, и мы должны полагаться на метод catch/try, но если это происходит часто, то есть вероятность, что нам нужно пересмотреть способ написания наших тестовых функций. –

+0

Это проблема с вашим тестом и/или кодом. Вы НЕ ожидаете общего RuntimeException, вы ожидаете конкретного исключения или, по крайней мере, определенного сообщения. –

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() 
) 
); 
0

Мне нравится ответ 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(); 

} 

Обратите внимание, что оставляя «провал» заявление в блоке Ьги вызывает соответствующее исключение утверждения быть пойманным; использование return в выписке исключает это.

 Смежные вопросы

  • Нет связанных вопросов^_^