Я хотел написать этот короткий пост, чтобы выделить функцию assertFailsWith
доступную Kotlin, которая немного облегчает тестирование исключений. Тестирование исключений не является чем-то необычным или новым для языков JVM (отныне я буду использовать Java для сравнений), но Kotlin предоставляет приятное дополнительное преимущество, заключающееся в предоставлении этой функциональности в составе стандартной библиотеки. Сравнивая это с Java, вы, вероятно, включите AssertJ в смесь, чтобы достичь аналогичных результатов.
Основная цель этого поста — assertFailsWith
функцией assertFailsWith
. Лично я не знал, что он существует некоторое время, и по умолчанию зависел от AssertJ. Не то чтобы я имел что-то против AssertJ. Есть много других функций, которые предоставляет библиотека, но для этого конкретного экземпляра можно было бы удалить его (при условии, что вы не используете его для чего-либо еще).
Что хорошего в assertFailsWith
и AssertJ в целом? Он обеспечивает лучшее тестирование исключений, чем простые конструкции, которые предоставляет JUnit. Точнее, он позволяет вам указать, какую часть вашего теста вы ожидаете, что будет сгенерировано исключение, вместо того, чтобы объявлять, что исключение возникнет где-то в коде. Это может привести к тому, что исключения будут некорректно проглочены тестом в неправильной точке и заставят вас думать, что это работает так, как вы думаете.
Теперь у меня есть краткое объяснение, давайте продолжим с основным содержанием этого поста. Ниже assertFailsWith
как выглядит assertFailsWith
внутри теста:
1
2
3
4
|
@Test fun `calling hereIsAnException will return an exception no matter what`() { assertFailsWith<IllegalArgumentException> { hereIsAnException() } } |
В этом примере hereIsAnException
помещается внутри тела assertFailsWith
, который проверяет, что выбрасывается IllegalArgumentException
. Если кто-то не поднят, то утверждение не будет выполнено. Если это произойдет, то утверждение пройдет, и исключение будет перехвачено.
Перехват исключения позволяет продолжить выполнение тестового кода, если это необходимо, а также позволяет делать дополнительные утверждения о состоянии исключения.
Например, является ли это оберткой вокруг другого исключения (каков тип его свойства cause
)?
1
2
3
4
5
|
@Test fun `original cause for exception was IndexOutOfBoundsException`() { val exception = assertFailsWith<IllegalArgumentException> { hereIsAnException() } assertTrue(exception.cause is IndexOutOfBoundsException) } |
Является ли сообщение тем, что вы ожидаете (не самая надежная из проверок)?
1
2
3
4
5
|
@Test fun `exception has the correct message`() { val exception = assertFailsWith<IllegalArgumentException> { hereIsAnException() } assertEquals( "I am a failure..." , exception.message) } |
assertFailsWith
только те исключения, которые относятся к тому же типу или assertFailsWith
который указан assertFailsWith
. Любые другие вызовут сбой теста. Так как он ловит RuntimeException
только Exception
или RuntimeException
. Постарайтесь быть точным, чтобы ваши тесты были максимально полезными.
Как уже assertFailsWith
ранее, assertFailsWith
будет перехватывать только исключение, которое выбрасывается в теле функции. Поэтому, если это было написано вместо:
1
2
3
4
5
|
@Test fun `calling hereIsAnException will return an exception no matter what`() { hereIsAnException() assertFailsWith<IllegalArgumentException> { hereIsAnException() } } |
Тест провалится. hereIsAnException
исключение, которое не было hereIsAnException
и приводит к hereIsAnException
теста. Я считаю, что это лучшая часть такого рода функций по сравнению с предыдущими способами (например, утверждение внутри @Test
том, что произойдет исключение).
1
2
3
4
|
@Test fun `calling hereIsAnException will return an exception no matter what`() { assertFailsWith<IllegalArgumentException>( "This should throw an illegal argument exception" ) { hereIsANormalReturnValue() } } |
Лично я никогда не использовал часть сообщения в утверждении. Может быть, так и есть, поэтому я подумал, что хотя бы дам вам знать.
Прежде чем я завершу небольшое количество контента в этом посте, давайте кратко рассмотрим AssertJ, чтобы мы могли провести сравнение между ними. Опять же, это только для случая перехвата исключений, который является лишь небольшой частью того, что предоставляет AssertJ.
1
2
3
4
5
6
|
@Test fun `calling hereIsAnException will return an exception no matter what`() { assertThatExceptionOfType(IllegalArgumentException:: class .java).isThrownBy { hereIsAnException() } } |
Это немного более «многословно», чем версия assertFailsWith
. Но это компенсируется множеством функций, которые предоставляет AssertJ, что делает любую дальнейшую проверку возвращенного исключения намного проще. Точнее, при использовании assertFailsWith
мне нужно было написать еще одно утверждение для проверки сообщения. В AssertJ это просто функция, прикованная к концу предыдущего вызова.
В заключение, assertFailsWith
— приятная небольшая функция, используемая при тестировании, чтобы гарантировать, что часть кода assertFailsWith
исключение определенного типа. Он встроен в стандартную библиотеку Kotlin, что устраняет необходимость вносить дополнительную зависимость в ваш проект. При этом, это относительно простая функция, которая не предоставляет такой функциональности, как библиотека вроде AssertJ. Скорее всего, этого будет достаточно, пока вы не захотите написать тесты, которые содержат широкий диапазон или утверждения, так как это может привести к путанице.
Официальные документы для assertFailsWith
можно найти здесь, если вы заинтересованы Kotlin Docs — assertFailsWith .
Опубликовано на Java Code Geeks с разрешения Дэна Ньютона, партнера нашей программы JCG . Смотрите оригинальную статью здесь: Тестирование исключений в Kotlin с помощью assertFailsWith Мнения, высказанные участниками Java Code Geeks, являются их собственными. |