Тестування JUnit. Приклади
Зміст
- 1. Поняття про модульне тестування в Java
- 2. Поняття тестового прикладу (Test Case). Клас, що містить тестові приклади (методи). Запуск тесту
- 3. Приклад, що демонструє використання JUnit для перевірки правильності розв’язку квадратного рівняння. Покрокова інструкція
- 4. Приклад, що демонструє послідовність виклику методів у JUnit-тесті
- Споріднені теми
Пошук на інших ресурсах:
1. Поняття про модульне тестування в Java
У системах програмування на мові Java, зокрема Java Eclipse, введено засоби тестування, які передбачають створення та використання автоматичних тестів для розроблених програм. Правильно побудоване тестування дозволяє звести кількість помилок у програмі до нуля.
При розробці сучасних програмних систем популярною є методологія TDD (Test Driven Development). Основна ідея застосування TDD полягає в тому, що спочатку розробляються тести на бажану поведінку програми (системи), а потім пишеться робочий програмний код, який з допомогою засобів рефакторингу перевіряється вже створеними тестами. Як наслідок, наперед підготовлені тести дозволяють уникнути здачі в експлуатацію програми, що містить помилки.
Для проведення тестування в Java Eclipse застосовується так зване JUnit-тестування, яке є фактичним стандартом для тестування Java-додатків. JUnit – це просте середовище (framework), яке дозволяє писати тести у вигляді класів на мові Java. Ці класи можуть запускатись як єдине ціле з допомогою засобу виконання тестів.
JUnit має багато розширень, які впроваджені у всіх сучасних середовищах розробки на мові Java. На сьогоднішній день JUnit використовується у таких напрямках як:
- модульне тестування;
- тестування веб-сайтів;
- автоматична підстановка інтерфейсів для тестування;
- тестування паралельного виконання додатків;
- тестування продуктивності.
JUnit-тестування використовується багатьма групами тестувальників (quality assurance) на спеціально-розроблених веб-сайтах, які забезпечують автоматизацію застосування наскрізних функціональних тестів.
В Java Eclipse використання механізму JUnit не потребує працюючого серверу додатків або діючої бази даних.
⇑
2. Поняття тестового прикладу (Test Case). Клас, що містить тестові приклади (методи). Запуск тесту
При використання механізму JUnit, розглядаються дві основні одиниці (класи):
- клас, методи якого потрібно протестувати;
- клас, який представляє собою JUnit тест. Цей клас побудований за спеціальними правилами.
Отже, JUnit-тест представляє собою спеціально розроблений клас, що містить методи, які тестують програмний код інших класів. У системі Java Eclipse цей клас створюється з допомогою команди JUnit Test Case (дивіться приклад нижче).
Клас містить ряд методів. Кожен з методів тестувального класу розглядається як окремий тестовий приклад – Test Case.
Тестові приклади (методи) JUnit-класу можуть бути оголошені з використанням наступних анотацій (розглядається версія Java Eclipse 2018-09):
- @BeforeAll – статичний метод (тестовий приклад), що оголошений з цією анотацією, викликається на початку тестування;
- @AfterAll – статичний метод, що оголошений з цією анотацією, викликається в кінці тестування. Тут можна задавати, наприклад, звільнення ресурсів що використовуються при тестуванні;
- @Test – метод, який оголошений з цією анотацією, є тестовим прикладом. Він містить безпосередньо код тестування. Цей метод використовує методи класу org.junit.jupiter.api.Assertions. У цьому методі використовуються методи порівняння, які починаються з префіксу assert*(). Наприклад, для порівняння двох значень будь-якого примітивного типу використовується перевантажений метод assertEquals(). В одному тестувальному класі JUnit може бути довільна кількість методів (прикладів) тестування, які оголошені з анотацією @Test;
- @BeforeEach – викликається перед викликом кожного тестового прикладу (методу), який оголошений з анотацією @Test;
- @AfterEach – викликається після завершення кожного тестового прикладу (методу), який оголошений з анотацією @Test.
Допускається довільна кількість методів з усіма вищенаведеними анотаціями.
У найбільш загальному випадку, приблизний вигляд класу, що згенерований системою Java Eclipse, може бути таким
class TestClass { @BeforeAll static void setUpBeforeClass() throws Exception { } @AfterAll static void tearDownAfterClass() throws Exception { } @BeforeEach void setUp() throws Exception { } @AfterEach void tearDown() throws Exception { } @Test void testMethod() { fail("Not yet implemented"); } }
У методі testMethod() потрібно вставити власний код перевірки роботи методів іншого класу (дивіться приклади нижче). Після цього, можна запускати тест з допомогою спеціальної команди Java Eclipse (дивіться приклад нижче).
⇑
3. Приклад, що демонструє використання JUnit для перевірки правильності розв’язку квадратного рівняння. Покрокова інструкція
3.1. Умова задачі
Використовуючи технологію JUnit розробити Unit-тест для перевірки роботи класу SquareEquation, який містить засоби розв’язку квадратного рівняння.
Тест повинен розміщуватись у класі SquareEquationTest та містити відповідні методи перевірки правильності отриманого розв’язку.
Корені квадратного рівняння повертаються у вигляді екземпляру класу Roots.
Клас SquareEquationTest містить відповідні засоби розв’язку квадратного рівняння.
⇑
3.2. Рішення
3.2.1. Створення класів SquareEquation та Roots
Перед створенням класів створюється проект стандартним для Java Eclipse способом. У проект додаються два класи з іменами SquareEquation та Roots та формується код цих класів. Відповідно створюється файл SquareEquation.java. Після виконаних дій, вікно Java Eclipse має вигляд як показано на рисунку 1.
Рисунок 1. Вікно Java Eclipse. Класи Roots та SquareEquation
Класи мають наступне призначення:
- Roots – використовується для збереження коренів квадратного рівняння (x1, x2) у випадку, якщо рівняння має ці корені;
- SquareEquation – реалізує методи розв’язку квадратного рівняння.
Клас SquareEquation має наступні складові:
- внутрішні змінні a, b, c що є коефіцієнтами квадратного рівняння;
- конструктор, що ініціалізує значення внутрішніх полів a, b, c;
- метод Solution(), який повертає екземпляр класу Roots у випадку, якщо рівняння має корені. Якщо рівняння не має коренів, то генерується виключення типу ArithmeticException.
Повний текст класів Roots та SquareEquation виглядає так
// Корені рівняння class Roots { public double x1; public double x2; } // Клас, який розв'язує квадратне рівняння - цей клас буде протестовано іншим класом. // a*x^2 + b*x + c = 0 public class SquareEquation { // Коефіцієнти рівняння private double a, b, c; // Конструктор public SquareEquation(double a, double b, double c) { this.a = a; this.b = b; this.c = c; } // Метод, що розв'язує квадратне рівняння public Roots Solution() { // Дискримінант double d = b*b - 4*a*c; // Перевірка, чи рівняння має корені if (d<0) throw new ArithmeticException("Solution has no roots."); // Обчислити результат Roots result = new Roots(); result.x1 = (-b - Math.sqrt(d)) / (2*a); result.x2 = (-b + Math.sqrt(d)) / (2*a); // Повернути результат return result; } }
Після створення класів можна переходити до створення Unit-тесту.
⇑
3.2.2. Створення класу, необхідного для реалізації JUnit тестування
Для створення Unit-тесту в Java Eclipse потрібно вибрати послідовність команд
File->New->JUnit Test Case
як показано на рисунку 2.
Рисунок 2. Команда створення Unit-тесту в Java
У результаті відкриється вікно “New JUnit Test Case” (рисунок 3) в якому потрібно вказати необхідну інформацію про тест.
Рисунок 3. Вікно створення нового тесту. Задавання параметрів тесту
При створенні вікна система автоматично заповнить деякі поля. У вікні вказується така інформація.
- У полі Source Folder вказується ім’я папки з вихідними файлами проекту. Система автоматично “підтягує” ім’я папки з поточним проектом.
- У полі Name – задається ім’я класу, в якому будуть розміщуватись методи тестування (з анотаціями @BeforeAll, @Test тощо). У нашому випадку пропонується SquareEquationTest (до імені класу SquareEquation додається закінчення Test). Запропоноване ім’я можна залишити без змін.
- У полі “Which method stubs would you like to create?” з допомогою опцій вказуються методи, які потрібно сформувати в класі. Відповідно до вибраної опції формуються наступні методи:
- setUpBeforeClass() – ім’я методу з анотацією @BeforeAll (дивіться п.2);
- setUp() – ім’я методу з анотацією @BeforeEach;
- tearDownAfterClass() – ім’я методу з анотацією @AfterAll;
- tearDown() – ім’я методу з анотацією @AfterEach.
Для нашого тесту достатньо активувати тільки одне поле setUp(). Це дасть змогу додати метод з анотацією @BeforeEach, що буде викликатись перед тестом. У цьому методі буде створюватись екземпляр класу SquareEquation (дивіться коди нижче).
- У полі “Class under test:” вказується ім’я класу SquareEquation, методи якого потрібно протестувати.
- Додатково задаються інші опції:
- вибір моделі тестування “New JUnit Jupiter test”;
- можливість додавання коментарів;
- інше.
У нашому випадку можна залишити все без змін.
Після вибору кнопки “Next >” відкриється наступне вікно “New JUnit Test Case” в якому потрібно задати перелік методів, які будуть тестуватись. Вигляд вікна зображено на рисунку 4. Вибрані методи будуть оголошуватись з анотацією @Test. Це є безпосередньо тестові приклади. Всі інші опції у вікні можна залишити за замовчуванням і перейти далі з допомогою кнопки Finish.
Рисунок 4. Вибір методів, що будуть тестуватись. Вибрано метод Solution()
⇑
3.2.3. Код класу SquareEquationTest
Після створення JUnit тесту з допомогою віконного інтерфейсу Java Eclipse згенерує код класу SquareEquationTest
import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class SquareEquationTest { @BeforeEach void setUp() throws Exception { } @Test void testSolution() { fail("Not yet implemented"); } }
У цьому коді сформовано два методи:
- метод setUp() з анотацією @BeforeEach. Цей метод буде викликатись першим при тестуванні;
- метод testSolution() з анотацією @Test. У цьому методі буде поміщено безпосередньо тест функції Solution() класу SquareEquation.
⇑
3.2.4. Модифікація класу SquareEquationTest. Програмування тесту
Щоб протестувати роботу класу SquareEquation потрібно створити екземпляр цього класу. Тому, в текст класу SquareEquationTest додатково вводиться посилання на клас SquareEquation
class SquareEquationTest { private SquareEquation se; ... }
Потім у методі setUp() створюється екземпляр класу SquareEquation, на який вказує посилання se
@BeforeEach void setUp() throws Exception { // Цей метод викликається першим. // Створити екземпляр класу SquareEquation. se = new SquareEquation(2, 1, -3); // 2*x^2 + x - 3 = 0 }
Після цього, в методі TestSolution() вписується код перевірки правильності отриманого розв’язку для заданих коефіцієнтів
a = 2 b = 1 c = -3
в екземплярі se
@Test void testSolution() { // Оголосити екземпляр класу Roots Roots rt = se.Solution(); // Перевірка розв'язку x1 assertEquals(rt.x1, -1.5); // Перевірка розв'язку x2 assertEquals(rt.x2, 1.0); }
Перевірка розв’язку здійснюється з допомогою перевантаженого методу assertEquals() класу org.junit.jupiter.api.Assertions. Відбувається порівняння розв’язку в змінних rt.x1 та rt.x2 з наперед обчисленим (вручну) розв’язком поточного варіанту (a = 2, b = 1, c = -3) квадратного рівняння. Якщо є співпадіння, то розв’язок правильний. Про те, що розв’язок правильний, потрібна сказати система після запуску тесту (наступний пункт).
В цілому текст всього модуля тестування наступний
import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class SquareEquationTest { // Посилання на клас SquareEquation private SquareEquation se; @BeforeEach void setUp() throws Exception { // Цей метод викликається першим. // Створити екземпляр класу SquareEquation se = new SquareEquation(2, 1, -3); // 2*x^2 + x - 3 = 0 } @Test void testSolution() { // Оголосити екземпляр класу Roots Roots rt = se.Solution(); // Перевірка розв'язку x1 assertEquals(rt.x1, -1.5); // Перевірка розв'язку x2 assertEquals(rt.x2, 1.0); } }
⇑
3.3. Запуск тесту засобами Java Eclipse. Перегляд результату тестування
Після того, як сформовано код класу SquareEquationTest, розроблений тест можна запускати на виконання. Це здійснюється командами
Run -> Run As -> JUnit Test
або викликом послідовності комбінацій клавіш Alt+Shift+X, T як показано на рисунку 5.
Рисунок 5. Запуск JUnit-тесту
Існує інший спосіб з допомогою команди швидкого меню (рисунок 6).
Рисунок 6. Команда запуску JUnit-тесту
Після запуску у лівій частині Java Eclipse у вікні JUnit відобразиться результат тестування (рисунок 7).
Рисунок 7. Результат тестування
Як видно з результату, отримано підтвердження правильного розв’язку, тобто тест складено. Отже, метод Solution() класу SquareEquation дає правильний результат для випадку коли a = 2, b = 1, c = -3.
Якщо в методі TestSolution() при виклику assertEquals() навмисно вказати невірну відповідь, наприклад
assertEquals(rt.x2, 777.777);
то після повторного запуску тесту, у вікні JUnit відобразиться помилка як видно на рисунку 8. Хоча це не є помилка програми, а навмисна помилка в тесті.
Рисунок 8. Демонстрація провалу тесту
⇑
4. Приклад, що демонструє послідовність виклику методів у JUnit-тесті
Нехай в системі Java Eclipse створено клас з іменем TestClass для проведення тестування деякого іншого класу. У даному прикладі, суть класу, що тестується, не має значення.
Програмний код класу наступний
import static org.junit.jupiter.api.Assertions.*; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; class TestClass { @BeforeAll static void setUpBeforeClass() throws Exception { System.out.println("BeforeAll"); } @BeforeAll static void setUpBeforeClass2() throws Exception { System.out.println("BeforeAll2"); } @AfterAll static void tearDownAfterClass() throws Exception { System.out.println("AfterAll"); } @BeforeEach void setUp() throws Exception { System.out.println("BeforeEach"); } @BeforeEach void setUp2() throws Exception { System.out.println("BeforeEach2"); } @AfterEach void tearDown() throws Exception { System.out.println("AfterEach"); } @Test void testSolution() { System.out.println("Test"); } @Test void testSolution2() { System.out.println("Test2"); } }
Після запуску тесту на виконання, програма видасть наступний результат
BeforeAll BeforeAll2 BeforeEach2 BeforeEach Test AfterEach BeforeEach2 BeforeEach Test2 AfterEach AfterAll
Проаналізувавши цей результат, можна прийти до висновку, що методи в тестувальному класі викликаються у порядку, що визначається їх анотаціями. Найпершими викликаються методи з анотацією @BeforeAll.
Наступними розглядаються тестові приклади, що оголошені з анотацією @Test. Перед кожним тестовим прикладом викликаються усі методи з анотацією @BeforeEach. Після кожного тестового прикладу викликаються усі методи з анотацією @AfterEach.
Останніми викликаються усі методи з анотацією @AfterAll.
⇑
Споріднені теми
⇑