Переходимо на JUnit 4 (вихідні коди)

Версія JUnit 4 відійшла від колишніх суворих угод про присвоєнні імен та ієрархій успадкування на користь раціональності і гнучкості анотацій Java ™ 5. У цьому навчальному посібнику, який доповнює популярну серію матеріалів щодо підвищення якості програмного коду, фахівець з тестування Ендрю Гловер (Andrew Glover) демонструє, як використовувати забезпечувані анотаціями нові можливості – параметризрвані тести, тести винятків і тести з обмеженням за часом. Крім того, цей навчальний посібник розповідає про гнучких фікстура JUnit 4 і показує, як використовувати анотації замість наборів (suite) для логічного групування тестів до початку їх виконання. У навчальний посібник включені кілька прикладів тестів, виконуваних в середовищі Eclipse, а також інструкції з виконання тестів JUnit 4 за допомогою старих, несумісних версій інструменту Ant.


Цілі документа


Це навчальний посібник крок за кроком демонструє фундаментальні концепції JUnit 4, при цьому основна увага приділяється анотаціям Java 5. Освоївши цей навчальний посібник, розраховане на одну годину занять, ви зрозумієте основні відмінності версії JUnit 4, а також познайомитеся з такими можливостями JUnit 4, Як тести винятків, параметризрвані тести і нова гнучка фікстурная модель. З цього посібника ви дізнаєтеся про те, як оголошувати тест, як використовувати анотації (замість наборів тестів) для логічного групування тестів перед виконанням і як запускати тести в середовищах Eclipse 3.2, Ant, а також з командного рядка.


Попередні умови


Щоб отримати максимальну віддачу від пропонованого навчального посібника, ви повинні мати загальне уявлення про розробку в середовищі Java. Крім того, матеріал навчального посібника написаний у припущенні, що читач усвідомлює значення тестування на етапі розробки і знайомий з основами зіставлення з шаблонами. Для успішного освоєння розділу, присвяченого виконанню тестів JUnit 4, Вам потрібно володіти навичками роботи з інтегрованим середовищем розробки Eclipse 3.2 і з інструментом Ant версії 1.6 або вище. Знайомство з попередніми версіями середовища JUnit для роботи з даними навчальним посібником не обов'язково.


Вимоги до системи


Для практичної роботи з представленим у навчальному посібнику програмним кодом необхідна працююча середу Sun JDK версії 1.5.0_09 (або вище) або IBM developer kit for Java technology версії 1.5.0 SR3. Для вивчення розділів допомоги, що стосуються прогону тестів JUnit 4 в середовищі Eclipse, необхідна працююча середовище Eclipse версії 3.2 або вище. Для розділів посібника, що стосуються прогону тестів за допомогою Ant, необхідний, відповідно, інструмент Ant версії 1.6 або вище.


Рекомендована конфігурація системи для роботи з даними навчальним посібником:



Вказівки даного навчального посібника викладені відповідно до операційної системи Microsoft Windows. Всі інструменти, розглянуті в навчальному посібнику, здатні також працювати в операційних системах Linux і UNIX.


Нові можливості JUnit 4


Введення анотацій Java 5 «Полегшило» технологію JUnit 4 і підвищило її гнучкість. Це дозволило відмовитися від суворих угод про присвоєнні імен та ієрархій успадкування на користь ряду нових вражаючих функцій. Нижче наведено короткий перелік новинок, що з'явилися у версії JUnit 4:



У першому розділі навчального посібника пояснюються найважливіші фундаментальні зміни, реалізовані у версії JUnit 4. Потім ми детально обговоримо ці та інші нові функції в наступних розділах.


Прощання з минулим


До появи у версії JUnit 4 анотацій Java 5 ця інфраструктура тестування спиралася на дві угоди, які мають найважливіше значення для її функціонування. Перша угода полягала в тому, що в JUnit неявним чином було потрібно, щоб ім'я будь-якого методу, призначеного для функціонування в якості логічного тесту, починалося з префікса test. Будь-який метод, ім'я якого починалося з цього префікса, наприклад, testUserCreate, Виконувався відповідно до добре описаним процесом тестування, який гарантував виконання відповідної фікстура (fixture) як до, так і після цього тестового методу. Друга угода, що дозволяє середовищі JUnit розпізнавати потрібний об'єкт – клас, що містить тести – полягала в тому, що сам цей клас повинен був бути розширенням класу TestCase середовища JUnit (або деяким похідним від нього). Тест, який порушував будь-яке з вказаних угод, не міг бути виконаний.


У лістингу 2 показаний тест JUnit, написаний до появи версії JUnit 4.


Лістинг 1. Невже все має бути настільки складно?





import java.util.regex.Matcher;
import java.util.regex.Pattern;

import junit.framework.TestCase;

public class RegularExpressionTest extends TestCase {

private String zipRegEx = “^d{5}([-]d{4})?$”;
private Pattern pattern;

protected void setUp() throws Exception {
this.pattern = Pattern.compile(this.zipRegEx);
}

public void testZipCode() throws Exception{
Matcher mtcher = this.pattern.matcher(“22101”);
boolean isValid = mtcher.matches();
assertTrue(“Pattern did not validate zip code”, isValid);
}
}


Нові можливості

У JUnit 4 за рахунок використання анотацій Java 5 вдалося повністю відмовитися обох вищевказаних угод. Відпадає необхідність в ієрархії класів, а методи, призначені для функціонування в Як тести, досить промаркувати нової анотацією @Test.


У лістингу 2 показаний той же тест, що і в лістингу 1, але переписаний з використанням анотацій


Лістинг 2. Тестування з застосуванням анотацій





import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertTrue;

public class RegularExpressionTest {
private static String zipRegEx = “^d{5}([-]d{4})?$”;
private static Pattern pattern;

@BeforeClass
public static void setUpBeforeClass() throws Exception {
pattern = Pattern.compile(zipRegEx);
}

@Test
public void verifyGoodZipCode() throws Exception{
Matcher mtcher = this.pattern.matcher(“22101”);
boolean isValid = mtcher.matches();
assertTrue(“Pattern did not validate zip code”, isValid);
}
}


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


Спрощення процесу документування


Один з корисних побічних ефектів анотацій полягає в тому, що анотації наочно документують все те, що повинен робити кожен метод – без необхідності глибокого розуміння внутрішньої моделі інфраструктури тестування. Що може бути наочніше, ніж маркування тестового методу анотацією @Test? Це істотне вдосконалення порівняно з попередніми версіями JUnit, для яких потрібно добре знання угод JUnit навіть якщо ви просто хотіли зрозуміти, який внесок вносить кожен метод у загальний тестовий сценарій (test case).


Анотації істотно допомагають при аналізі раніше написаного тесту, проте вони виявляються ще корисніше в процесі написання нових тестів.


Тестування з застосуванням анотацій


Підтримка анотацій Java 5 істотно відрізняє JUnit 4 від попередніх версій. У цьому розділі я познайомлю вас із використанням анотацій у таких ключових галузях, як оголошення тесту, тестування виключень і тестування з обмеженням за часом, а також ігнорування небажаних або невикористовуваних тестів.


Оголошення тесту


Оголошення тесту в JUnit 4 зводиться до маркування тестового методу анотацією @Test. Зверніть увагу (лістинг 3), що немає необхідності у спадкуванні від будь-якого спеціального класу.


Лістинг 3. Оголошення тесту в JUnit 4





import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertFalse;

public class RegularExpressionTest {
private static String zipRegEx = “^d{5}([-]d{4})?$”;
private static Pattern pattern;

@BeforeClass
public static void setUpBeforeClass() throws Exception {
pattern = Pattern.compile(zipRegEx);
}

@Test
public void verifyZipCodeNoMatch() throws Exception{
Matcher mtcher = this.pattern.matcher(“2211”);
boolean notValid = mtcher.matches();
assertFalse(“Pattern did validate zip code”, notValid);
}
}


Зауваження щодо статичного імпорту


У наведеному вище прикладі (лістинг 3) я використав підтримувану в Java 5 функцію статичного імпорту для імпортування методу assertFalse() класу Assert. Це пояснюється тим, що у версії JUnit 4 тестові класи не успадковують від класу TestCase, Як це було в попередніх версіях JUnit.


Тестування винятків


Як і в попередніх версіях JUnit, настійно рекомендується вказати тесту на необхідність видачі певних винятків (Exception). Єдина ситуація, коли це правило не застосовується – якщо ви збираєтеся протестувати саме конкретний. Якщо тест видає виключення, інфраструктура (framework) тестування повідомляє про негативному результаті тестування.


При необхідності тестування конкретного винятку слід використовувати запропоновану в версії JUnit 4 анотацію @Test з параметром expected. Цей параметр призначений для представлення типу виключення, яке даний тест повинен видавати в процесі виконання.


Просте порівняння наочно показує, що дає цей новий параметр.


Тестування винятків у версії JUnit 3.8


У лістингу 4 показаний тест testZipCodeGroupException() для середовища JUnit 3.8, який перевіряє, чи не призведе спроба отримати третю групу оголошеного мною регулярного вираження до виключення IndexOutOfBoundsException:


Лістинг 4. Тестування винятків у версії JUnit 3.8





import java.util.regex.Matcher;
import java.util.regex.Pattern;

import junit.framework.TestCase;

public class RegularExpressionTest extends TestCase {

private String zipRegEx = “^d{5}([-]d{4})?$”;
private Pattern pattern;

protected void setUp() throws Exception {
this.pattern = Pattern.compile(this.zipRegEx);
}

public void testZipCodeGroupException() throws Exception{
Matcher mtcher = this.pattern.matcher(“22101-5051”);
boolean isValid = mtcher.matches();
try{
mtcher.group(2);
fail(“No exception was thrown”);
}catch(IndexOutOfBoundsException e){
}
}
}


У цій старій версії JUnit для такого простого тесту мені довелося написати неабияку кількість коду – а саме конструкцію try/catch та умови невдачі тесту на випадок, якщо виключення не буде зафіксовано.


Тестування винятків у версії JUnit 4


Показаний в лістингу 5 тест виключення відрізняється від тесту в лістингу 4 тільки використанням параметра expected. (Зверніть увагу, що для модифікації тесту з лістингу 4 мені достатньо було помістити виняток IndexOutOfBoundsException в анотацію @Test.)


Лістинг 5. Тестування виключень за параметром expected





import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.junit.BeforeClass;
import org.junit.Test;

public class RegularExpressionJUnit4Test {
private static String zipRegEx = “^d{5}([-]d{4})?$”;
private static Pattern pattern;

@BeforeClass
public static void setUpBeforeClass() throws Exception {
pattern = Pattern.compile(zipRegEx);
}

@Test(expected=IndexOutOfBoundsException.class)
public void verifyZipCodeGroupException() throws Exception{
Matcher mtcher = this.pattern.matcher(“22101-5051”);
boolean isValid = mtcher.matches();
mtcher.group(2);
}
}


Тестування з обмеженням за часом


У версії JUnit 4 в якості параметра тестового сценарію (test case) може бути використане значення ліміту часу (timeout). Як можна побачити з лістингу 6, значення timeout представляє максимальну кількість часу, відведеного на виконання даного тесту: при перевищенні цього ліміту часу тестування завершується невдачею.


Лістинг 6. Тестування з обмеженням за часом





@Test(timeout=1)
public void verifyFastZipCodeMatch() throws Exception{
Pattern pattern = Pattern.compile(“^d{5}([-]d{4})?$”);
Matcher mtcher = pattern.matcher(“22011”);
boolean isValid = mtcher.matches();
assertTrue(“Pattern did not validate zip code”, isValid);
}

Організувати тестування з обмеженням за часом нескладно – для створення автоматизованого тесту з обмеженням за часом досить після анотації @Test вказати значення параметра timeout.


Ігнорування тестів


До появи версії JUnit 4 ігнорування невдалих або незавершених тестів представляло певну проблему. Якщо ви хотіли, щоб середовище проігнорувала певний тест, вам необхідно було змінити ім'я цього тесту таким чином, щоб воно не відповідало системі позначення тестів. Наприклад, щоб вказати, що даний тест в даний момент виконувати не потрібно, я зазвичай вставляв символ «_» перед ім'ям тестового методу.


У версії JUnit 4 передбачена анотація @Ignore, Яка змушує інфраструктуру тестування проігнорувати даний тестовий метод. Можна також вставити коментар до вашого рішення про ігнорування тесту – для розробників, які можуть згодом випадково зіткнутися з цим тестом.


Анотація @ Ignore


У лістингу 7 показано, наскільки просто проігнорувати тест, регулярне вираження якого поки що не працює:


Лістинг 7. Ігнорування тесту





@Ignore(“this regular expression isn”t working yet”)
@Test
public void verifyZipCodeMatch() throws Exception{
Pattern pattern = Pattern.compile(“^d{5}([-]d{4})”);
Matcher mtcher = pattern.matcher(“22011”);
boolean isValid = mtcher.matches();
assertTrue(“Pattern did not validate zip code”, isValid);
}

Все записано!


Як показано на рис. 1, спроба прогону цього тесту в середовищі Eclipse (наприклад) приведе до появи повідомлення про проігнороване тесті.


Рис. 1. Представлення проігнороване тесту в середовищі Eclipse


Тестові фікстура (Test fixture)


Тестові фікстура існували і до версії JUnit 4, проте в цій версії фікстурная модель суттєво оновлено і вдосконалена. У цьому розділі пояснюється, навіщо і в яких випадках доцільно застосовувати фікстура, і демонструються відмінності між негнучкими фікстура попередніх версій і нової фікстурной моделлю у версії JUnit 4.


Для чого потрібні фікстура


Фікстура допомагають багаторазово використовувати програмний код за рахунок правила, яке гарантує виконання певної логіки до або після виконання тесту. У попередніх версіях JUnit це правило неявно малося на увазі незалежно від реалізації фікстура розробником. У версії JUnit 4 фікстура вказуються в явному вигляді за допомогою анотацій, іншими словами, правило реалізується тільки в тому випадку, якщо ви дійсно вирішили використовувати відповідну фікстура.


За допомогою правил, що гарантують виконання фікстура до або після прогону тесту, можна створювати програмний код для багаторазового використання. Приміром, такий код може ініціалізувати клас, який ви бажаєте протестувати в декількох тестових сценаріях, і навіть наповнювати базу даних перед прогоном залежного від даних тесту. У будь-якому випадку використання фікстура гарантовано покращує керованість тестового сценарію – завдяки тому, що цей сценарій спирається на загальну логіку.


Фікстура особливо корисні, коли ви проганяєте безліч тестів, що використовують однакову логіку, і деякі або всі з цих тестів закінчуються невдачею. Замість того, щоб аналізувати вихідну логіку в кожному тесті, вам досить знайти помилку в одному місці. Крім того, у разі, коли частина тестів проходить успішно, а частина закінчується невдачею, вам не потрібно аналізувати як джерело невдач логіку фікстура.


Негнучкі фікстура


У попередніх версіях JUnit застосовувалася негнучка фікстурная модель, при якій кожен тестовий метод необхідно було обрамляти методами setUp() і tearDown(). Можливі негативні сторони цієї моделі демонструються в лістингу 8, де метод setUp() реалізується і потім виповнюється двічі – для кожного заданого тесту.


Лістинг 8. Негнучкі фікстура





import java.util.regex.Matcher;
import java.util.regex.Pattern;

import junit.framework.TestCase;

public class RegularExpressionTest extends TestCase {

private String zipRegEx = “^d{5}([-]d{4})?$”;
private Pattern pattern;

protected void setUp() throws Exception {
this.pattern = Pattern.compile(this.zipRegEx);
}

public void testZipCodeGroup() throws Exception{
Matcher mtcher = this.pattern.matcher(“22101-5051”);
boolean isValid = mtcher.matches();
assertEquals ("group (1) didn" t equal -5051 "," -5051 ", mtcher.group (1));
}

public void testZipCodeGroupException() throws Exception{
Matcher mtcher = this.pattern.matcher(“22101-5051”);
boolean isValid = mtcher.matches();
try{
mtcher.group(2);
fail(“No exception was thrown”);
}catch(IndexOutOfBoundsException e){
}
}
}


Обхідні шляхи


Як показано в лістингу 9, в попередніх версіях JUnit можна було вказати, щоб фікстура виконувалася тільки один раз, за допомогою «декоратора» TestSetup. Однак цей спосіб був досить обтяжливим (зверніть увагу, що в даному випадку необхідний метод suite()).


Лістинг 9. Застосування TestSetup в попередніх версіях JUnit





import java.util.regex.Matcher;
import java.util.regex.Pattern;

import junit.extensions.TestSetup;
import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import junit.textui.TestRunner;

public class OneTimeRegularExpressionTest extends TestCase {

private static String zipRegEx = “^d{5}([-]d{4})?$”;
private static Pattern pattern;

public static Test suite() {
TestSetup setup = new TestSetup(
new TestSuite(OneTimeRegularExpressionTest.class)) {
protected void setUp() throws Exception {
pattern = Pattern.compile(zipRegEx);
}
};
return setup;
}

public void testZipCodeGroup() throws Exception {
Matcher mtcher = pattern.matcher(“22101-5051”);
boolean isValid = mtcher.matches();
assertEquals ("group (1) didn" t equal -5051 "," -5051 ", mtcher.group (1));
}

public void testZipCodeGroupException() throws Exception {
Matcher mtcher = pattern.matcher(“22101-5051”);
boolean isValid = mtcher.matches();
try {
mtcher.group(2);
fail(“No exception was thrown”);
} catch (IndexOutOfBoundsException e) {
}
}
}


Досить сказати, що в попередніх версіях JUnit складність використання фікстура часто переважує переваги від їх застосування.


Гнучкі можливості версії JUnit 4.0


Завдяки використанню анотацій у версії JUnit 4 накладні витрати, пов'язані з фікстура, істотно скорочені. Анотації дозволяють виконувати одну й ту ж фікстура для кожного тесту або всього один раз для всього класу, або не виконувати її зовсім. Передбачено чотири анотації фікстура – дві для фікстура рівня класу і дві для фікстура рівня методу. На рівні класу використовуються фікстура @BeforeClass і @AfterClass, А на рівні методу (або тесту) використовуються фікстура @Before і @After.


У тестовий сценарій в лістингу 10 включена фікстура, яка за допомогою анотації @Before виконується для обох тестів.


Лістинг 10. Гнучкі фікстура на основі анотацій





import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;

public class RegularExpressionJUnit4Test {
private static String zipRegEx = “^d{5}([-]d{4})?$”;
private static Pattern pattern;

@Before
public static void setUpBeforeClass() throws Exception {
pattern = Pattern.compile(zipRegEx);
}

@Test
public void verifyZipCodeNoMatch() throws Exception{
Matcher mtcher = this.pattern.matcher(“2211”);
boolean notValid = mtcher.matches();
assertFalse(“Pattern did validate zip code”, notValid);
}

@Test(expected=IndexOutOfBoundsException.class)
public void verifyZipCodeGroupException() throws Exception{
Matcher mtcher = this.pattern.matcher(“22101-5051”);
boolean isValid = mtcher.matches();
mtcher.group(2);
}
}


Одноразові фікстура


Припустимо, вам необхідно виконати фікстура всього один раз. Замість того, щоб застосовувати декоратор, як це робилося в попередніх версіях (див. лістинг 9), ви можете використовувати анотацію @BeforeClass, Як показано в лістингу 11:


Лістинг 11. Одноразова фікстура в JUnit 4





import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertFalse;

public class RegularExpressionJUnit4Test {
private static String zipRegEx = “^d{5}([-]d{4})?$”;
private static Pattern pattern;

@BeforeClass
public static void setUpBeforeClass() throws Exception {
pattern = Pattern.compile(zipRegEx);
}

@Test
public void verifyZipCodeNoMatch() throws Exception{
Matcher mtcher = this.pattern.matcher(“2211”);
boolean notValid = mtcher.matches();
assertFalse(“Pattern did validate zip code”, notValid);
}

@Test(expected=IndexOutOfBoundsException.class)
public void verifyZipCodeGroupException() throws Exception{
Matcher mtcher = this.pattern.matcher(“22101-5051”);
boolean isValid = mtcher.matches();
mtcher.group(2);
}
}


Функціональність TearDown у версії JUnit 4


Зазначимо, що метод tearDown() з попередніх версій JUnit не зник і в новій фікстурной моделі. Щоб відтворити функціональність методу tearDown(), Досить створити який-небудь новий метод і застосувати анотацію @After або @AfterClass.


Гнучкість у квадраті


Версія JUnit 4 дозволяє вказати для тестового сценарію більше однієї фікстура. Нові фікстура на основі анотацій не перешкоджають створенню декількох фікстурних методів @BeforeClass. Проте слід враховувати, що у версії JUnit 4 не можна вказати, який фікстурний метод повинен виконуватися в першу чергу. Це може привести до ускладнень в разі, якщо ви збираєтеся використовувати більше одного фікстурного методу.


Виконання тестів JUnit 4


До чудових особливостей оновленою і вдосконаленою версією JUnit 4 відноситься відсутність т.зв. наборів тестів (Test suite) – механізму, який використовувався в попередніх версіях для логічного групування тестів для подальшого спільного виконання. У цьому розділі я представлю нові оптимізовані анотації, що прийшли на зміну розділами, і покажу, як проганяти тести JUnit 4 в середовищі Eclipse і з допомогою інструмента Ant.


Набір тестів з попередніх версій JUnit


Щоб зрозуміти різницю, подивіться на набір тестів з попередніх версій JUnit, показаний в лістингу 12 (цей набір групує два логічних тестових класу і виконує їх як єдине ціле).


Лістинг 12. Набір тестів з попередніх версій JUnit





import junit.framework.Test;
import junit.framework.TestSuite;

public class JUnit3Suite {

public static Test suite() {
TestSuite suite = new TestSuite();
suite.addTest(OneTimeRegularExpressionTest.suite());
suite.addTestSuite(RegularExpressionTest.class);
return suite;
}
}


Дві нові корисні анотації


У версії JUnit 4 на зміну семантиці набору тестів прийшли дві нові анотації. Перша з них – @RunWith – Дозволяє для прогону конкретного тестового класу застосовувати інших «виконавців» (runner) замість виконавців, вбудованих в інфраструктуру тестування. У комплект JUnit 4 входить виконавець наборів тестів – клас під назвою Suite – Який необхідно вказувати в анотації @RunWith. Крім того, необхідна ще одна анотація під назвою @SuiteClasses, Параметром якої є список класів, що представляють набір тестів.


Лістинг 13. Дуже корисні анотації





import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;

@RunWith(Suite.class)
@SuiteClasses({ParametricRegularExpressionTest.class,
RegularExpressionTest.class,
TimedRegularExpressionTest.class})
public class JUnit4Suite {

}






 



Не турбуйтеся, якщо нові анотації JUnit 4 здаються вам незрозумілими. По всій видимості, творці JUnit 4 самі переживають подібні почуття. У Javadocs вони пояснюють, що планують допрацьовувати API-інтерфейс виконавця (runner) «в міру одержання інформації про те, як люди використовують його на практиці». Принаймні чесно!
 


Прогін тестів JUnit 4 в середовищі Eclipse


Тестові класи JUnit 4 можна виконувати як з допомогою інтегрованого середовища розробки, наприклад, Eclipse, так і за допомогою інтерфейсу командного рядка. Для запуску тестів JUnit в середовищі Eclipse версії 3.2 і вище необхідно вибрати варіант тестування Run As JUnit. Для прогону тесту за допомогою командного рядка необхідно виконати клас org.junit.runner.JUnitCore, Передавши йому як аргумент повне ім'я тесту.


Наприклад, якщо при роботі в Eclipse ви не хочете використовувати вбудований виконавець JUnit, ви можете задати нову конфігурацію прогону, попередньо поставивши клас JUnitCore (Див. рис. 2).


Рис. 2. Перший крок для прогону тесту JUnit 4 в Eclipse за допомогою командного рядка
 


Вказівка тесту


Потім необхідно вказати тест для прогону, для чого потрібно додати повне ім'я відповідного тесту у вікні Program arguments на вкладці Arguments (див. рис. 3).


Рис. 3. Другий крок для прогону тесту JUnit 4 в Eclipse за допомогою командного рядка


Інструмент Ant і JUnit 4


Ant і JUnit вже давно є прекрасною парою, і багато розробників очікували, що з появою версії JUnit 4 ці відносини тільки зміцняться. Однак виникли несподівані складнощі. У версіях Ant нижче 1.7 виконувати тести JUnit 4 в незмінному вигляді неможливо. Це не означає, що їх не можна виконати в принципі – їх просто необхідно модифікувати.


Невідповідний поєднання


Прогін тесту JUnit 4 (лістинг 14) у Ant (версії нижче 1.7) дає цікаві результати:


Лістинг 14. Простий тестовий клас JUnit 4





import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.assertTrue;

public class RegularExpressionTest {
private static String zipRegEx = “^d{5}([-]d{4})?$”;
private static Pattern pattern;

@BeforeClass
public static void setUpBeforeClass() throws Exception {
pattern = Pattern.compile(zipRegEx);
}

@Test
public void verifyGoodZipCode() throws Exception{
Matcher mtcher = this.pattern.matcher(“22101”);
boolean isValid = mtcher.matches();
assertTrue(“Pattern did not validate zip code”, isValid);
}
}


Провал за всіма показниками


Запуск звичного завдання junit в Ant дає помилки, показані в лістингу 15.


Лістинг 15. Безліч помилок





[junit] Running test.com.acme.RegularExpressionTest
[Junit] Tests run: 1, Failures: 1, Errors: 0, Time elapsed: 0.047 sec
[junit] Testsuite: test.com.acme.RegularExpressionTest
[Junit] Tests run: 1, Failures: 1, Errors: 0, Time elapsed: 0.047 sec

[junit] Testcase: warning took 0.016 sec
[junit] FAILED
[junit] No tests found in test.com.acme.RegularExpressionTest
[junit] junit.framework.AssertionFailedError: No tests found in
test.com.acme.RegularExpressionTest
[junit] Test test.com.acme.RegularExpressionTest FAILED


Обхідний маневр


Якщо ви хочете виконувати тести JUnit 4 в Ant версії нижче 1.7, необхідно доповнити ваш набір тестів методом suite() , Який буде повертати екземпляр JUnit4TestAdapter (Див. лістинг 16).


Лістинг 16. Нове застосування старого методу





public static junit.framework.Test suite(){
return new JUnit4TestAdapter(RegularExpressionTest.class);
}

У цьому примірнику необхідно повністю визначити повертається тип Test, Оскільки є анотація з подібним ім'ям @Test. При наявності методу suite() будь-яка версія Ant виконає всі ваші тести JUnit 4 без проблем!







 

Параметризрвані тестування


Для перевірки бізнес-логіки додатків регулярно доводиться створювати тести, кількість яких може істотно коливатися. У попередніх версіях JUnit це призводило до значних незручностей – головним чином через те, що зміна груп параметрів в тестованому методі вимагало написання окремого тестового сценарію для кожної групи.


У версії JUnit 4 реалізована нова прекрасна можливість, що дозволяє створювати загальні тести, в які можна направляти різні значення параметрів. У результаті ви можете створити один тестовий сценарій і прогнати його кілька разів – по одному разу для кожного параметра.


Простота параметризованих тестування


Створення параметричного тесту в JUnit 4 виробляється в п'ять простих кроків:



  1. Створення типового тесту без конкретних значень параметрів.
  2. Створення методу static, Який повертає тип Collection і маркує його анотацією @Parameter.
  3. Створення членів класу для типів параметрів, які потрібні для типового методу, описаного в кроці 1.
  4. Створення конструктора, які пов'язує ці типи параметрів з відповідними членами класу, описаними на кроці 3.
  5. Вказівка тестового сценарію, який необхідно виконувати разом з класом Parameterized (Досягається використанням анотації @RunWith).

Розглянемо зазначені кроки по черзі.


Крок 1. Створення типового тесту


У лістингу 17 показаний типовий тест для перевірки різних значень на відповідність регулярним виразом. Зверніть увагу, що на даний момент значення phrase і match не визначені.

Лістинг 17. Типовий тест




@Test
public void verifyGoodZipCode() throws Exception{
Matcher mtcher = this.pattern.matcher(phrase);
boolean isValid = mtcher.matches();
assertEquals ("Pattern did not validate zip code", isValid, match);
}

Крок 2. Створення методу введення параметрів


На цьому кроці створюється метод введення параметрів, і оголошений як static та повертає тип Collection. Цей метод необхідно забезпечити анотацією @Parameters. Усередині цього методу вам досить створити багатовимірний масив Object і перетворити його в список List, Як показано в лістингу 18:

Лістинг 18. Метод введення параметрів з анотацією @ Parameters




@Parameters
public static Collection regExValues() {
return Arrays.asList(new Object[][] {
{“22101”, true },
{“221×1”, false },
{“22101-5150”, true },
{“221015150”, false }});
}

Крок 3. Створення двох членів класу


Оскільки параметри мають типи String і boolean, На наступному кроці ви створюєте два члени класу.

Лістинг 19. Оголошення двох членів класу




private String phrase;
private boolean match;

Крок 4. Створення конструктора


Конструктор, який ви створите на цьому кроці, зв'яже члени класу з вашими значеннями параметрів (див. лістинг 20):

Лістинг 20. Конструктор для зв'язування значень




public ParametricRegularExpressionTest (String phrase, boolean match) {
this.phrase = phrase;
this.match = match;
}

Крок 5. Завдання класу Parameterized


І, нарешті, на рівні класу необхідно вказати, що даний тест повинен виконуватися разом з класом Parameterized, Як показано в лістингу 21:

Лістинг 21. Специфікування класу Parameterized і анотації @ RunWith




@RunWith(Parameterized.class)
public class ParametricRegularExpressionTest {
//…
}


Прогін тесту


При виконанні цього тестового класу типової тестовий метод verifyGoodZipCode() виповнюється чотири рази – по одному разу для кожної пари значень, визначених у методі введення даних regExValues(), Див. лістинг 18.


Якщо, наприклад, цей тест проганяється в середовищі Eclipse, то вона повідомить про чотири прогонах (див. рис. 4).


Рис. 4. Прогін параметризованих тестів у середовищі Eclipse


Інші новинки


Крім важливих змін, описаних вище, у версії JUnit 4 реалізований і ряд менш значних, зокрема, запроваджено новий метод assert і виключено термінальний стан.


Новий метод assert


У версії JUnit 4 доданий новий метод assert для порівняння вмісту масивів. Ця зміна не є значним, тим не менше, вам більше не доведеться проходити по всьому вмісту масиву і перевіряти кожен окремий елемент.


Наприклад, показаний в лістингу 22 код був неможливий у попередніх версіях JUnit. У попередніх версіях JUnit цей тестовий сценарій не зміг би працювати внаслідок незначних відмінностей у другому елементі кожного масиву.


Лістинг 22. У версії JUnit 4 метод assertEquals підтримує роботу з масивами





@Test
public void verifyArrayContents() throws Exception{
String [] actual = new String [] {"JUnit 3.8.x", "JUnit 4", "TestNG"};
String [] var = new String [] {"JUnit 3.8.x", "JUnit 4.1", "TestNG 5.5"};
assertEquals ("the two arrays should not be equal", actual, var);
}


Тепер – без помилок!


Невелике, але приємна зміна у версії JUnit 4 полягає у відмові від поняття «помилка». У той час як попередні версії JUnit повідомляли і про кількість невдач, і про кількість помилок, у версії JUnit 4 тест або проходить успішно, вже існують або невдачею.


Що цікаво, одночасно з виключенням одного стану з'явилося і одне нове – внаслідок можливості ігнорування тестів. При прогоні серії тестів середу JUnit 4 повідомляє про кількість невдач і про кількість проігнорованих тестів.


Висновок


Радикальний перегляд вихідної концепції у версії JUnit 4 не означає, що ця інфраструктура тестування тепер функціонує абсолютно по-іншому – міць і простота вихідної інфраструктури залишаються незмінними. І дійсно, при більш глибокому вивченні інфраструктури JUnit 4 ви виявите, що, незважаючи на появу в цій версії серії нових вражаючих функцій, в ній не принесені в жертву будь-які базові принципи, які свого часу викликали революцію в області тестування на етапі розробки.


У цьому посібнику ми крок за кроком познайомилися з версією JUnit 4 – Від оголошення тесту до параметризованих тестування. Ви відкрили для себе такі нові можливості, як тестування з обмеженням за часом і тестування винятків, і дізналися про зміни в таких знайомих вам поняттях, як фікстура і логічне групування. Крім того, ви побачили, як виглядає прогін тесту в середовищі Eclipse, та ознайомилися з простим прийомом, який дозволить вам виконувати тести в інструменті Ant будь-якої версії, навіть раніше версії 1.7.


Я сподіваюся, що ви засвоїли основну думку даного посібника – анотації ні в якій мірі не зменшують можливості середовища JUnit і суттєво спрощують її використання. Випробуйте анотації в дії – вони істотно прискорюють написання і прогін тестів!

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*