Unit тести у Java. Короткий посібник

Unit тести у Java. Короткий посібник

  • 25 вересня, 2023
Платон	Цыбульский
Платон Цыбульский Java developer у Brain agency

Кожна виконана задача e програмуванні вимагає тестування, тому що від помилок, як відомо, ніхто не застрахований. Найчастіше на цю процедуру йде чимало часу, навіть у простих завданнях у новачків. Ви запускаєте додаток, вводите дані для перевірки і розумієте, що результат не відповідає очікуванням. Потім ви починаєте з'ясовувати, на якому ж етапі сталася помилка, все це у вас забирає дорогоцінні хвилини, які ви могли б витратити на розробку нового функціоналу. А що, якщо ваш додаток великий і в ньому багато що залежать один від одного модулів, класів і компонентів і ваше завдання — змінити поведінку існуючого коду, при цьому не пошкодивши старе.

У чому сенс Unit-тестів?

Саме для цього придумали unit-тести, які дають можливість автоматизувати перевірку додатку. Але ж на написання тестів теж йде час — заперечите ви і матимете рацію. Справа в тому, що з ростом вашого застосування цей час буде скорочуватися, а без тестів ситуація зворотна: чим складніше стає додаток, тим більш трудомістким буде його зміна і процес стеження за вадами. До того ж, не будемо забувати, що інтеграційне тестування, яким користується більшість «смертних» програмістів, вимагає запуску програми, а, як відомо, у Java розгортання та збирання — процес не найшвидший. Пам'ятається мені один банківський проект, в якому я витрачав на це 15 хвилин, але не будемо про сумне.

Для того, щоб перейнятися цією концепцією, пропоную почитати про екстремальне програмування. А поки, давайте розглянемо, які інструменти нам пропонує Java для вирішення цієї проблеми. Найбільш популярні — JUnit і TestNg, і мова сьогодні піде про перший. Він є простим і гнучким фреймворком для тестування.

Рекомендуємо курс по темі

JUnit анотації та їх опис

Для того, щоб підключити JUnit, нам необхідно додати dependency у pom.xml, на момент написання статті найсвіжіша версія 4.12:

Потім представимо, для простоти розуміння, що у нас є клас калькулятор, який може додавати і віднімати:

Unit тест для такого класу буде виглядати так:

Розміщуватися даний клас повинен у папці test, яка спеціально створена для зберігання тестових класів. Назва класу має відповідати одній з масок: * Test, Test *, * TestCase для того, щоб maven surefire plugin зміг знайти ваш тестовий клас. Методи повинні мати повертаємий тип void в сигнатурі і анотацію @Test, яка визначає, що метод є тестовим. Анотація @Before вказує на те, що метод буде виконуватися перед кожним тестованим методом. Також для перевірки результату використовується спеціальний перевіряємий метод assertEquals, який в нашому випадку може приймати повідомлення, яке буде показуватися при невідповідності фактичного і очікуваного результату, потім другий параметр — фактичний результат і третій очікуваний результат.

Далі відкриваємо термінал, перейдемо у папку з нашим проектом і виконаємо mvn test:

За висновком лога консолі видно, що тести пройшли успішно. Якщо ж ми змінимо знак у методі add c + на *, то тест буде failed і лог буде виглядати так:

Таким чином, ми відразу бачимо, який тест у нас не пройшов перевірку, і можемо почати налагодження з потрібної точки.

Рекомендуємо курс по темі

У наступній таблиці наведено огляд наявних у анотації JUnit 4.x.

Анотація Опис
@Test public void method() public void method() public void method() public static void method() public static void method() Анотація @Test визначає що метод method () є тестовим.
@Before Анотація @Before вказує на те, що метод буде виконуватися перед кожним тестованим методом @Test.
@After Анотація @After вказує на те що метод буде виконуватися після кожного тестованого методу @Test
@BeforeClass Анотація @BeforeClass вказує на те, що метод буде виконуватися на початку всіх тестів, а точніше у момент запуску тестів (перед усіма тестами @Test).
@AfterClass Анотація @AfterClass вказує на те, що метод буде виконуватися після всіх тестів.
@Ignore Анотація @Ignore каже, що метод буде проігнорований у момент проведення тестування.
@Test (expected = Exception.class) (expected = Exception.class) — вказує на те, що у даному тестовому методі ви навмисно очікуєте Exception.
@Test (timeout=1000) (timeout=1000) — вказує, що тестований метод не повинен займати більше ніж 1 секунду.


Методи (основні), що перевіряються

Метод Опис
fail(String) Вказує на те, щоб тестовий метод завалився, при цьому виводячи текстове повідомлення.
assertTrue([message], boolean condition) Перевіряє, що логічна умова істинна.
assertsEquals([String message], expected, actual) Перевіряє, що два значення збігаються. Для масивів перевіряються посилання, а не зміст масивів.
assertNull([message], object) Перевіряє, що об'єкт є порожнім null.
assertNotNull([message], object) Перевіряє, що об'єкт не є порожнім null.
assertSame([String], expected, actual) Перевіряє, що обидві змінні відносяться до одного об'єкту.
assertNotSame([String], expected, actual) Перевіряє, що обидві змінні відносяться до різних об'єктів.

Також я прикріпив приклад з ООП моделлю компанії, у якій підраховуються витрати на зарплату для співробітників по компанії і департаменту. Основні методи покриті unit тестами, код розміщений тут:

https://bitbucket.org/platon_tsy/unittest/overview