Іноді виникнення виключення є очікуваним поведінкою системи, і в тестах потрібно перевіряти, що воно дійсно виникає.
Нижче описані п'ять способів, як в тестовому фреймворку JUnit перехопити очікуване виняток і перевірити його властивості. Перші чотири з них можна використовувати в JUnit 4, а останній спосіб використовує нові можливості JUnit 5.
Як приклад для демонстрації візьмемо тест для функції стандартної бібліотеки, що створює тимчасовий файл. Будемо перевіряти, що при спробі створення файлу в неіснуючої директорії виникає виняток типу IOException. При цьому попередньо в тому ж самому тесті створюється тимчасова директорія і тут же видаляється, так що ми отримуємо гарантовано неіснуючу директорію, в якій і намагаємося створити файл:
Зрозуміло, в такому вигляді тест впаде, а в звіті буде написано, що виникло виключення. А нам потрібно, щоб тест в цьому випадку навпаки позначався як успішний. Подивимося, як це можна виправити.
Найпростіший спосіб повідомити тестовому фреймворку про те, що очікується виняток - вказати додатковий параметр expected в анотації @Test:
Цей параметр повинен містити тип очікуваного виключення. Якщо виникне виключення саме такого типу - тест пройде успішно. Якщо виникне виключення іншого типу або не виникне зовсім - тест впаде.
- Не можна перевірити текст повідомлення або інші властивості виник виключення.
- Не можна зрозуміти, де саме виникло виключення. У розглянутому прикладі воно могло бути викинуто НЕ тестируемой функцією, а трохи раніше, при спробі створити тимчасову директорію. Тест навіть не зміг дістатися до виклику тестируемой функції - але при цьому в звіті він позначається як успішно пройдений!
Друга зі згаданих проблем настільки жахлива, що я нікому ніколи не рекомендую використовувати цей спосіб.
2. try-catch
Обидва нестачі можна усунути, якщо перехоплювати виключення явно за допомогою конструкції try-catch:
Якщо виключення виникає до блоку try - тест падає, ми дізнаємося про те, що у нього виникли проблеми.
Якщо тестована функція не викидає взагалі ніякого виключення - ми потрапляємо на fail () в наступному рядку, тест падає.
Якщо вона викидає виключення невідповідного типу - блок catch не ловить його, тест знову таки падає.
Успішно він завершується тільки тоді, коли тестована функція викидає виключення потрібного типу.
Тест став більш надійним, він більше не пропускає баги. А в блоці catch можна перевірити властивості спійманого виключення.
Однак працювати з конструкцією try-catch незручно.
Щоб позбутися від неї, можна скористатися правилом ExpectedException. входять в стандартний дистрибутив JUnit 4:
Тепер код має просту плоску структуру, хоча загальна кількість рядків коду, на жаль, збільшилася.
Але головна проблема цього способу полягає в тому, що перевірки в такому стилі виглядають протиприродно - спочатку описується поведінка, а потім викликається функція. Звичайно, це справа смаку, але мені подобається, коли перевірки розташовуються після виклику тестируемой функції.
4. AssertJ / catch-throwable
Більш красивий спосіб, який використовує можливості Java 8, пропонують додаткові бібліотеки, такі як AssertJ або catch-throwable. Ось приклад роботи з AssertJ:
А якщо виняток не виникне - "пастка" сама викине виняток і тест впаде.
5. JUnit 5
Але чому потрібно використовувати якісь додаткові бібліотеки, чому тестові фреймворки самі не пропонують зручних можливостей для роботи з очікуваними винятками?
Уже надають. Перехоплення винятків в JUnit 5 виглядає дуже схоже на попередній приклад:
Раніше така можливість в JUnit була відсутня, тому що попередні версії JUnit були орієнтовані на більш старі версії Java, де не було лямбда-виразів і написати подібний код було просто неможливо. Так, можна зробити щось подібне за допомогою анонімних класів, але це виглядає настільки жахливо, що конструкція try-catch здається верхом витонченості.
Так що якщо вам доводиться писати тести, в яких перевіряється виникнення виключень - є привід придивитися до нових можливостей JUnit 5.
Share on Twitter Share on Facebook Share on Google+