Как я могу идиоматически использовать JUnit4, чтобы проверить, что какой-то код выдает исключение?
Хотя я, конечно, могу сделать что-то вроде этого:
@Test
public void testFooThrowsIndexOutOfBoundsException() {
boolean thrown = false;
try {
foo.doStuff();
} catch (IndexOutOfBoundsException e) {
thrown = true;
}
assertTrue(thrown);
}
Я помню, что есть аннотация, Assert.xyz или что-нибудь, которые гораздо менее беспорядочные и гораздо более соответствуют духу JUnit для такого рода ситуаций.
Вы можете увидеть, как тестировать исключения на вики-странице JUnit github.com/junit-team/junit/wiki/Exception-testing
@ZeroOne - для этого у меня было бы два разных теста - один для исключения, а другой для проверки взаимодействия с вашим макетом.
Есть способ сделать это с помощью JUnit 5, я обновил свой ответ ниже.




Как насчет этого: поймайте очень общее исключение, убедитесь, что оно выходит из блока catch, а затем подтвердите, что класс исключения такой, каким вы его ожидаете. Это утверждение не будет выполнено, если а) исключение неправильного типа (например, если вместо этого вы получили нулевой указатель) и б) исключение не было создано.
public void testFooThrowsIndexOutOfBoundsException() {
Throwable e = null;
try {
foo.doStuff();
} catch (Throwable ex) {
e = ex;
}
assertTrue(e instanceof IndexOutOfBoundsException);
}
Кроме того, вы не увидите, какое исключение находится в результатах теста, когда наступит день, когда тест не пройден.
Это можно немного улучшить, изменив способ утверждения в конце. assertEquals(ExpectedException.class, e.getClass()) покажет вам ожидаемые и фактические значения, когда тест не пройден.
JUnit имеет встроенную поддержку для этого с "ожидаемый" атрибут.
Это зависит от версии JUnit и используемых вами assert-библиотек.
Первоначальный ответ для 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.
Справка :
Этот фрагмент кода не будет работать, если вы ожидаете исключение только где-то в вашем коде, а не такое одеяло, как это.
@skaffman Это не будет работать с org.junit.experimental.theoriesTheory, созданным org.junit.experimental.theoriesTheories
@skaffman: Есть ли способ сделать подобное в junit 3.8, ожидая, что метод вызовет исключение?
Рой Ошеров не одобряет такого рода тестирование исключений в Искусство модульного тестирования, поскольку исключение может быть где угодно внутри теста, а не только внутри тестируемого модуля.
согласен с точкой зрения @Kiview, этот способ может не тестировать то, что вы хотели проверить. ложные срабатывания - это боль.
Я не согласен с @Kiview / Roy Osherove. На мой взгляд, тесты должны быть ориентированы на поведение, а не на реализацию. Проверяя, что конкретный метод может вызывать ошибку, вы привязываете свои тесты непосредственно к реализации. Я бы сказал, что тестирование показанным выше методом дает более ценный тест. Я хотел бы добавить предостережение: в этом случае я буду тестировать настраиваемое исключение, чтобы знать, что получаю исключение, которое мне действительно нужно.
@nickbdyer Конечно, вы хотите проверить поведение. Но вы хотите проверить поведение конструктора ArrayList? или метода get()? В приведенном выше примере вы тестируете оба. Тест будет пройден, если ваш конструктор вызовет это исключение, хотя вы хотите протестировать метод get().
Ни один. Я хочу проверить поведение класса. Важно то, что если я пытаюсь получить то, чего нет, я получаю исключение. Тот факт, что структура данных - это ArrayList, отвечающая get(), не имеет значения. Если бы в будущем я решил перейти к примитивному массиву, мне пришлось бы изменить эту тестовую реализацию. Структура данных должна быть скрыта, чтобы тест мог сосредоточиться на поведении класс.
Этот вопрос появляется в Google при поиске исключений JUnit. Может ли кто-нибудь отредактировать этот ответ и предоставить информацию, связанную с функциями JUnit 4.7? stackoverflow.com/a/16725280/1199832
@OhChinBoon Вы можете создать свой собственный подкласс Exception или RuntimeException и уловить это. А затем убедитесь, что ваш подкласс не используется более чем в одном месте.
public void testIndexOutOfBoundsException () выбрасывает IndexOutOfBoundsException {...}.
Не могли бы вы взглянуть, пожалуйста stackoverflow.com/questions/48567562/…
Это прекрасно работает! .... но кажется, что когда вы используете такого рода решения, на самом деле вы делаете неправильные вещи с исключениями. Пожалуйста, прочтите связь и новое решение3. Ошибки и исключения не должны прерывать модульное тестирование.
Я думаю, что этот способ тоже законен, это просто личное предпочтение. но также приятно упомянуть, что это может «перехватить» все исключения этого типа в вашем тесте, и иногда будет более точным перехватить исключение внутри теста вокруг вызова определенного метода, где вы ожидаете возникновения исключения. так что, если вы помните об этом, вполне законно написать такой тест
@nickbdyer Я думаю, здесь какое-то недопонимание. Кивив / Кевин Виттек говорит, что любой метод, используемый в тесте, может вызвать исключение и привести к тому, что тест будет пройден без достижения оператора, который фактически должен генерировать исключение этого типа. Используемая структура данных не имеет отношения к его точке зрения. Если структура заключена в метод тестируемого класса, вы правы в том, что не имеет значения, какой оператор внутри этого класса генерирует исключение, но он не противоречил этому.
Будьте осторожны при использовании ожидаемого исключения, потому что оно только утверждает, что метод сгенерировало это исключение, а не конкретная строка кода в тесте.
Я обычно использую это для проверки параметров проверки, потому что такие методы обычно очень просты, но для более сложных тестов лучше использовать:
try {
methodThatShouldThrow();
fail( "My method didn't throw when I expected it to" );
} catch (MyException expectedException) {
}
Применяйте суждение.
Может, я олдскульный, но все же предпочитаю это. Это также дает мне возможность протестировать само исключение: иногда у меня есть исключения с геттерами для определенных значений, или я могу просто искать определенное значение в сообщении (например, ищу «xyz» в сообщении «нераспознанный код 'xyz» ").
Я думаю, что подход NamshubWriter дает вам лучшее из обоих миров.
+1 полезно в некоторых сценариях, где ожидаемый = xx не соответствует требованиям.
Используя ExpectedException, вы можете вызвать N exception.expect для каждого метода для проверки, как это исключение.expect (IndexOutOfBoundsException.class); foo.doStuff1 (); exception.expect (IndexOutOfBoundsException.class); foo.doStuff2 (); exception.expect (IndexOutOfBoundsException.class); foo.doStuff3 ();
@ user1154664 На самом деле нельзя. Используя ExpectedException, вы можете проверить только то, что один метод вызывает исключение, потому что, когда этот метод вызывается, тест перестанет выполняться, потому что он сгенерировал ожидаемое исключение!
Ваше первое предложение не соответствует действительности. При использовании ExpectedException обычно нужно установить ожидание непосредственно перед строкой, в которой вы ожидаете выбросить исключение. Таким образом, если предыдущая строка выдает исключение, правило не запускается, и тест завершается неудачно.
Это подходило для моего случая. Я хотел проверить состояние некоторых объектов после того, как было выбрано исключение, что не могло быть выполнено с «ожидаемым», поскольку оно существует только после того, как исключение было выбрано.
А также это совместимо с Arquillian-Tests. Я пробовал это с @Rule Annotation, но получил ошибку инициализации с Arquillian.
Кроме того, с этим подходом мы могли бы добавить больше утверждений в обработку исключений для проверки состояния, если это необходимо.
Согласен, правило ExpectedException подходит не для каждой ситуации.
Все еще использую это в 2019 году. Позволяет мне запускать другой проверочный код после выброса исключения (в противном случае - завершения выполнения).
Проблема с этим подходом заключается в том, что покрытие кода никогда не подберет эту строку кода.
Редактировать: Теперь, когда выпущены 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().
Подробнее см. эта статья.
@skaffman - Если я правильно понял, похоже, что exception.expect применяется только в одном тесте, а не во всем классе.
Если исключение, которое мы ожидаем сгенерировать, является проверенным исключением, должны ли мы добавлять выбросы или try-catch или тестировать эту ситуацию другим способом?
IMHO ответ от ууу ниже - гораздо лучшее решение (code.google.com/p/catch-exception), т.е. в этом примере, когда вы throw new NullPointerException(); после foo.doStuff(), тест нет завершится неудачно с NPE.
@MartinTrummer Никакой код не должен запускаться после foo.doStuff (), поскольку генерируется исключение и метод завершается. Наличие кода после ожидаемого исключения (за исключением закрытия ресурсов в finally) в любом случае бесполезно, поскольку он никогда не должен выполняться, если возникает исключение.
@Jason Thompson: "should" - это хорошо, но не пуленепробиваемое: с verify-exception вы можете быть уверены, что тест в порядке, даже если кто-то добавил какой-то код после doStuff(): когда новый код проходит, тест тоже и что еще более важно: когда новый код выдает ошибку, тест завершится неудачно. данное "решение" скроет этот факт (и, кроме того, verify-exception более краток и, следовательно, менее подвержен ошибкам):
@MartinTrummer То, что я пытаюсь понять, - это то, как код может выполняться после создания исключения. Возможно, вы можете привести пример. Насколько я понимаю, как только возникает исключение, метод завершается с исключением. Итак, если бы у меня была функция, которая выглядела бы так: void myMethod {throw new IllegalStateException (); executeOtherMethod (); } Насколько я знаю, метод executeOtherMethod () не может быть вызван. Так что это не проблема. Этот код не меняет принцип работы java, а скорее сообщает исполнителю тестов, что метод вызовет исключение и некоторые его характеристики.
@Jason, но это не тот код, который у вас есть. у вас есть foo.doStuff(); bar.doStuff(); - и вы ожидаете, что foo.doStuff(); выкинет. поэтому, если он бросает, bar.doStuff() никогда не выполняется (это плохо). но еще хуже бывает, когда код меняется и foo.doStuff() больше не выкидывает. теперь возможно, что bar.doStuff() бросает вызов - тест проходит - и вы не замечаете, что новый код фактически сломал исходные ожидания, которые бросает foo.doStuff();.
@JasonThompson: обычно в тестовой среде исключение проглатывается, и вы утверждаете, что оно было выброшено. В этом случае тест не завершается.
Когда я прокомментировал ответ rwoo, я думаю, что недостатком его библиотеки является то, что вы не можете проксировать каждый объект [например, классы, которые являются final?] ... но я не уверен, не попробовав его.
Это лучший подход. Здесь есть два преимущества по сравнению с решением Скаффмана. Во-первых, у класса ExpectedException есть способы сопоставления сообщения об исключении или даже написания собственного сопоставителя, который зависит от класса исключения. Во-вторых, вы можете установить свое ожидание непосредственно перед строкой кода, которая, как вы ожидаете, вызовет исключение - это означает, что ваш тест завершится неудачно, если неправильная строка кода вызовет исключение; тогда как с помощью решения skaffman это сделать невозможно.
Не работает, когда мы расширяем TestCase, тест не проходит, хотя исключение выдается, как ожидалось
@ singe31 OP спросил о JUnit4, и тесты в стиле JUnit4 не должны расширять TestCase
@DavidWallace проблема, с которой я столкнулся с этим подходом, заключается в том, что вы хотите подтвердить или проверить какое-либо поведение после выполненных методов (например, убедиться, что какое-либо фиктивное поведение имело место или не произошло), вы не можете этого сделать, потому что ваш тест завершит выполнение, когда исключение будет сгенерировано . Это может привести к неожиданному поведению теста, если у вас есть verify после вызова метода, который, по вашему мнению, выполняется, но на самом деле это не так.
@MikeKobit - Так вы проверяете, возникает ли исключение, или вы тестируете что-то еще? Я убежденный сторонник подхода «один тестовый пример, одно утверждение» - то есть, если вы тестируете генерирование исключения, не должно быть никаких других утверждений или проверок, происходящих после исключения.
@DavidWallace У меня уже был случай, когда я хочу протестировать метод на компоненте, который вызовет исключение. Я также хочу убедиться, что побочных эффектов не было. Если я использую макет фреймворка, например Mockito, с ответом выше, я не смогу проверить какое-либо поведение. Пример демонстрации того, о чем я говорю - stackoverflow.com/questions/13224288/…
@MikeKobit Хорошо, честно, звучит разумно. Я полагаю, что это будет довольно редкий тестовый пример.
Похоже, что у инструмента покрытия Eclipse есть проблемы с этим (не то чтобы это действительно важно)
@MJafarMash, если исключение, которое вы ожидаете выбросить, проверено, вы должны добавить это исключение в предложение throws тестового метода. Вы делаете то же самое каждый раз, когда тестируете метод, который, как объявлено, генерирует проверенное исключение, даже если исключение не запускается в конкретном тестовом случае.
@DavidWallace, как ExpectedException проверяет сообщение об исключении? Я ничего не вижу в API 4.12 для этого.
Вы можете передать Hamcrest Matcher на expect для анализа исключения. Или даже проще - вы можете передать String в expectMessage, который, как вы ожидаете, будет подстрокой сообщения. junit.org/junit4/javadoc/4.12/org/junit/rules/…
Вы можете сделать это так: exception.expectMessage("foo bar");
Как установить сообщение об ошибке для теста, если исключение не сгенерировано?
Чтобы решить ту же проблему, я создал небольшой проект: http://code.google.com/p/catch-exception/
Используя этот маленький помощник, вы могли бы написать
verifyException(foo, IndexOutOfBoundsException.class).doStuff();
Это менее подробное правило, чем правило ExpectedException в JUnit 4.7. По сравнению с решением, предоставленным skaffman, вы можете указать, в какой строке кода вы ожидаете исключение. Надеюсь, это поможет.
Я тоже думал о том, чтобы сделать что-то подобное, но в конце концов обнаружил, что истинная сила ExpectedException заключается в том, что вы можете не только указать ожидаемое исключение, но также можете указать определенные свойства исключения, такие как ожидаемая причина или ожидаемое сообщение.
Я предполагаю, что это решение имеет те же недостатки, что и моки? Например, если foo - это final, произойдет сбой, потому что вы не можете проксировать foo?
Том, если doStuff () является частью интерфейса, прокси-подход будет работать. Иначе такой подход не удастся, вы правы.
Я пробовал здесь многие методы, но они были либо сложными, либо не совсем соответствовали моим требованиям. Фактически, можно довольно просто написать вспомогательный метод:
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();
}
});
Отсутствие зависимостей: нет необходимости в mockito, нет необходимости в powermock; и отлично работает с финальными классами.
Интересно, но не подходит для AAA (Arrange Act Assert), где вы хотите выполнять действия Act и Assert на разных этапах.
@ bln-tom Технически это два разных шага, просто они не в таком порядке. ;п
Вы также можете сделать это:
@Test
public void testFooThrowsIndexOutOfBoundsException() {
try {
foo.doStuff();
assert false;
} catch (IndexOutOfBoundsException e) {
assert true;
}
}
В тестах JUnit лучше использовать Assert.fail(), а не assert, на всякий случай, если ваши тесты выполняются в среде, где утверждения не включены.
Просто сделайте Матчер, который можно включать и выключать, например:
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();
В моем случае я всегда получаю RuntimeException от db, но сообщения различаются. И исключение необходимо обрабатывать соответственно. Вот как я это тестировал:
@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));
}
перед строкой с } catch ( следует вставить fail("no exception thrown");
import static com.googlecode.catchexception.apis.BDDCatchException.*;
@Test
public void testFooThrowsIndexOutOfBoundsException() {
when(() -> foo.doStuff());
then(caughtException()).isInstanceOf(IndexOutOfBoundsException.class);
}
eu.codearte.catch-exception:catch-exception:2.0
Как уже было сказано ранее, в JUnit есть много способов работы с исключениями. Но с Java 8 есть еще один: использование лямбда-выражений. С помощью лямбда-выражений мы можем добиться такого синтаксиса:
@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
Раскрытие информации: я являюсь автором блога и проекта.
Мне нравится это решение, но могу ли я загрузить его из репозитория maven?
@Airduster одна из реализаций этой идеи, доступная в Maven, - stefanbirkner.github.io/vallado
@CristianoFontes - более простая версия этого API, запланированная для JUnit 4.13. См. github.com/junit-team/junit/commit/…
@RafalBorowiec технически new DummyService()::someMethod - это MethodHandle, но этот подход одинаково хорошо работает с лямбда-выражениями.
@NamshubWriter, похоже, от junit 4.13 отказались в пользу junit 5: stackoverflow.com/questions/156503/…
@Vadzim JUnit 4.13 не заброшен. Команда понимает, что некоторые люди не могут перейти на JUnit 5, и в 4.13 есть несколько хороших исправлений, но большая часть недавних усилий была сосредоточена на базе кода 5.x.
Junit 4.13 отсутствует, также существует его версия с assertJ и google-true, как указано в других ответах здесь.
Мы можем использовать ошибку утверждения после метода, который должен возвращать исключение:
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());
}
Второй catch проглотит трассировку стека, если будет выбрано какое-либо другое исключение, потеряв полезную информацию.
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 может показаться немного сильным для некоторых, поэтому assertThat(e.getMessage(), containsString("the message"); может быть предпочтительнее.
В дополнение к тому, что сказал NamShubWriter, убедитесь, что:
Сделайте нет так:
@Rule
public ExpectedException expectedException;
@Before
public void setup()
{
expectedException = ExpectedException.none();
}
Наконец, сообщение в блоге это ясно показывает, как утверждать, что возникло определенное исключение.
В junit есть четыре способа проверить исключение.
для junit5.x вы можете использовать assertThrows следующим образом
@Test
public void testFooThrowsIndexOutOfBoundsException() {
Throwable exception = assertThrows(IndexOutOfBoundsException.class, () -> foo.doStuff());
assertEquals("expected messages", exception.getMessage());
}
для junit4.x используйте необязательный атрибут 'expected' аннотации Test
@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();
}
}
вы также можете использовать классический способ try / catch, широко используемый в рамках junit 3
@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 руководство пользователя для подробностей.
Для меня это лучший ответ, он очень четко охватывает все способы, спасибо! Лично я продолжаю использовать 3-й вариант даже с Junit4 для удобства чтения, чтобы избежать пустого блока catch, вы также можете поймать Throwable и подтвердить тип e
Можно ли использовать ExpectedException для ожидания проверенного исключения?
Все это совокупность трех основных ответов. ИМО, этот ответ даже не должен был публиковаться, если он не добавляет ничего нового. Просто отвечу (популярный вопрос) представителю. Довольно бесполезно.
конечно, потому что вы можете передать любой тип, производный от Trowable, в метод ExpectedException.expect. пожалуйста, смотрите это подпись. @miuser
Это лучший ответ
Я хотел бы отметить, что Junit 4.13 (акцент на «.13») ... поддерживает assertThrows. Я поддерживаю, потому что это хороший "полный обзор" ответ, но я надеюсь, что вы примете во внимание предостережение 4.13.
Если вам нужно решение, которое:
Вот служебная функция, которую я написал:
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!" );
}
Использование утверждения AssertJ, которое можно использовать вместе с JUnit:
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), потому что он гарантирует, что ожидаемая строка в тесте вызвала исключение, и позволяет вам проверить более подробную информацию об исключении, например сообщение, проще:
assertThatThrownBy(() ->
{
throw new Exception("boom!");
})
.isInstanceOf(Exception.class)
.hasMessageContaining("boom");
Инструкции Maven / Gradle здесь.
самый лаконичный способ, и это никому не нравится, странно .. У меня только одна проблема с библиотекой assertJ, которая конфликтует по именам с junit. больше о assertJ throwby: JUnit: тестирование исключений с Java 8 и AssertJ 3.0.0 ~ Codeleak.pl
@ycomp Ну, это новый ответ на очень старый вопрос, поэтому разница в баллах обманчива.
Вероятно, это лучшее решение, если можно использовать Java 8 и AssertJ!
@ycomp Я подозреваю, что этот конфликт имен может быть умышленным: поэтому библиотека AssertJ настоятельно рекомендует никогда не использовать JUnit assertThat, всегда AssertJ. Кроме того, метод JUnit возвращает только «обычный» тип, тогда как метод AssertJ возвращает подкласс AbstractAssert ... позволяя объединять методы, как указано выше (или каковы бы ни были технические термины для этого ...).
@weston на самом деле я только что использовал вашу технику в AssertJ 2.0.0. Нет никаких оправданий тому, что вы не обновили, без сомнения, но вы, возможно, хотели бы знать.
@mikerodent "разрешая привязку методов, как указано выше (или каковы бы ни были технические термины для этого ...)" свободный был бы термином. Итак, "... разрешая беглые высказывания".
@mikerodent, интересно, что он работает в 2, я не знаю, почему я подумал, что было 3
Я хотел прокомментировать свое решение этой проблемы, чтобы избежать необходимости в коде JUnit, связанном с исключениями.
Я использовал assertTrue (boolean) в сочетании с try / catch, чтобы найти ожидаемое исключение, которое должно быть сгенерировано. Вот пример:
public void testConstructor() {
boolean expectedExceptionThrown;
try {
// Call constructor with bad arguments
double a = 1;
double b = 2;
double c = a + b; // In my example, this is an invalid option for c
new Triangle(a, b, c);
expectedExceptionThrown = false; // because it successfully constructed the object
}
catch(IllegalArgumentException e) {
expectedExceptionThrown = true; // because I'm in this catch block
}
catch(Exception e) {
expectedExceptionThrown = false; // because it threw an exception but not the one expected
}
assertTrue(expectedExceptionThrown);
}
Второй catch проглотит трассировку стека, если будет выбрано какое-либо другое исключение, потеряв полезную информацию.
@Test
void testFooThrowsIndexOutOfBoundsException() {
Throwable exception = expectThrows( IndexOutOfBoundsException.class, foo::doStuff );
assertEquals( "some message", exception.getMessage() );
}
Дополнительная информация о JUnit 5 на http://junit.org/junit5/docs/current/user-guide/#writing-tests-assertions
Возьмем, к примеру, вы хотите написать 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 4 или новее вы можете протестировать исключения следующим образом
@Rule
public ExpectedException exceptions = ExpectedException.none();
предоставляет множество функций, которые можно использовать для улучшения наших тестов JUnit.
Если вы видите пример ниже, я тестирую 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");
}
}
Обновлять: JUnit5 имеет улучшение для тестирования исключений: assertThrows.
Следующий пример взят из: Руководство пользователя Junit 5
@Test
void exceptionTesting() {
Throwable exception = assertThrows(IllegalArgumentException.class, () ->
{
throw new IllegalArgumentException("a message");
});
assertEquals("a message", exception.getMessage());
}
Оригинальный ответ с использованием JUnit 4.
Есть несколько способов проверить возникновение исключения. Я также обсудил следующие варианты в моем сообщении Как писать отличные модульные тесты с JUnit
Установите параметр expected@Test(expected = FileNotFoundException.class).
@Test(expected = FileNotFoundException.class)
public void testReadFile() {
myClass.readFile("test.txt");
}
Использование trycatch
public void testReadFile() {
try {
myClass.readFile("test.txt");
fail("Expected a FileNotFoundException to be thrown");
} catch (FileNotFoundException e) {
assertThat(e.getMessage(), is("The file test.txt does not exist!"));
}
}
Тестирование по правилу ExpectedException.
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void testReadFile() throws FileNotFoundException {
thrown.expect(FileNotFoundException.class);
thrown.expectMessage(startsWith("The file test.txt"));
myClass.readFile("test.txt");
}
Вы можете узнать больше о тестировании исключений в JUnit4 wiki для тестирования исключений и bad.robot - Ожидание исключений Правило JUnit.
В 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 5 и JUnit 4.13, лучшим вариантом будет использование Assertions.assertThrows() (для JUnit 5) и Assert.assertThrows() (для JUnit 4.13). Видеть
файл Руководство пользователя Junit 5.
Вот пример, который проверяет возникновение исключения и использует Правда для утверждения сообщения об исключении:
public class FooTest {
@Test
public void doStuffThrowsIndexOutOfBoundsException() {
Foo foo = new Foo();
IndexOutOfBoundsException e = assertThrows(
IndexOutOfBoundsException.class, foo::doStuff);
assertThat(e).hasMessageThat().contains("woops!");
}
}
Преимущества перед подходами в других ответах:
throws.Аналогичный метод будет добавлен в org.junit Assert в JUnit 4.13.
Это чистый подход, но я не понимаю, как он позволяет нашему тесту следовать «Arrange-Act-Assert», поскольку мы должны заключить часть «Act» в «assertThrow», который является assert.
@Clockwork Лямбда - это «действие». Цель Arrange-Act-Assert - сделать код чистым и простым (а значит, простым для понимания и обслуживания). Как вы сказали, это чистый подход.
Я все еще надеялся, что смогу подтвердить бросок и исключение в конце теста, в части «assert». В этом подходе вам нужно заключить акт в первое утверждение, чтобы сначала его поймать.
Это потребовало бы большего количества кода в каждом тесте для выполнения утверждения. Это больше кода и будет подвержено ошибкам.
Мое решение с использованием лямбда-выражений Java 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);
Есть два способа написать тестовый пример
@Test(expected = IndexOutOfBoundsException.class)Вы можете просто перехватить исключение в тестовом классе с помощью блока try catch и подтвердить сообщение, которое генерируется методом в тестовом классе.
try{
}
catch(exception to be thrown from method e)
{
assertEquals("message", e.getmessage());
}
Я надеюсь это ответит на ваш вопрос Удачного обучения ...
Самый гибкий и элегантный ответ для Junit 4 я нашел в Блог Mkyong. Он имеет гибкость try/catch с использованием аннотации @Rule. Мне нравится этот подход, потому что вы можете читать определенные атрибуты настроенного исключения.
package com.mkyong;
import com.mkyong.examples.CustomerService;
import com.mkyong.examples.exception.NameNotFoundException;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.Matchers.hasProperty;
public class Exception3Test {
@Rule
public ExpectedException thrown = ExpectedException.none();
@Test
public void testNameNotFoundException() throws NameNotFoundException {
//test specific type of exception
thrown.expect(NameNotFoundException.class);
//test message
thrown.expectMessage(is("Name is empty!"));
//test detail
thrown.expect(hasProperty("errCode")); //make sure getters n setters are defined.
thrown.expect(hasProperty("errCode", is(666)));
CustomerService cust = new CustomerService();
cust.findByName("");
}
}
Решение Junit4 с Java8 должно использовать эту функцию:
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).
Я рекомендую библиотеку assertj-core для обработки исключений в тесте junit
В java 8 вот так:
//given
//when
Throwable throwable = catchThrowable(() -> anyService.anyMethod(object));
//then
AnyException anyException = (AnyException) throwable;
assertThat(anyException.getMessage()).isEqualTo("........");
assertThat(exception.getCode()).isEqualTo(".......);
try {
my method();
fail( "This method must thrwo" );
} catch (Exception ex) {
assertThat(ex.getMessage()).isEqual(myErrormsg);
}
Вы отвечаете на вопрос десятилетней давности, пожалуйста, расширите свой ответ, почему вы думаете, что он предлагает что-то новое, что принятый ответ не охватывает. Также всегда старайтесь рассказать что-нибудь о своем коде и предоставить документацию или объяснение.
По сути, это дубликат этот 10-летний ответ (за исключением проверки сообщения об ошибке).
Я не знаю, почему голосовал против. Нет дублирования. Пожалуйста, ознакомьтесь с проверкой сообщения об ошибке. Это правильный ответ, который я использовал сегодня со своей командой @hellow
Не всегда один и тот же человек жалуется на ваш вопрос / ответ и отвергает вас (как в этом случае, я этого не делал, но, пожалуйста, примите мои слова и расширите свой ответ).
Я не уверен, что тест, зависящий только от сообщения об ошибке, является хорошей практикой.
Да, это хорошая практика и полезная, когда у вас много сообщений об ошибках, так что вы убедитесь, что это конкретное сообщение будет выброшено (много возможных исключений)
Пожалуйста, что вы имеете в виду под расширенным ответом Hebrow, это ответ на вопрос, поэтому, если вы хорошо понимаете вопрос и код на нем, поэтому ответ будет простым, ему нужен только какой-то код
@Test(expectedException=IndexOutOfBoundsException.class)
public void testFooThrowsIndexOutOfBoundsException() throws Exception {
doThrow(IndexOutOfBoundsException.class).when(foo).doStuff();
try {
foo.doStuff();
} catch (IndexOutOfBoundsException e) {
assertEquals(IndexOutOfBoundsException .class, ex.getCause().getClass());
throw e;
}
}
Вот еще один способ проверить, вызвал ли метод правильное исключение.
Фреймворк JUnit имеет метод assertThrows():
ArithmeticException exception = assertThrows(ArithmeticException.class, () ->
calculator.divide(1, 0));
assertEquals("/ by zero", exception.getMessage());
Проблема с любым другим подходом, но это то, что они неизменно заканчивают тест после того, как было выброшено исключение. Я, с другой стороны, часто все еще хочу вызвать
org.mockito.Mockito.verifyс различными параметрами, чтобы убедиться, что определенные вещи произошли (например, была вызвана служба регистратора с правильными параметрами) до того, как было сгенерировано исключение.