Плутанина JUnit: використовувати "розширює TestCase" або "@Test"?


152

Я вважаю правильне використання (або принаймні документацію) JUnit дуже заплутаним. Це питання слугує як подальшим посиланням, так і реальним питанням.

Якщо я правильно зрозумів, є два основні підходи до створення та запуску тесту JUnit:

Підхід до A (JUnit 3-style): створити клас, який розширює TestCase, і запустити методи тестування зі словом test. Запускаючи клас як тест JUnit (у Eclipse), всі методи, що починаються зі слова test, автоматично запускаються.

import junit.framework.TestCase;

public class DummyTestA extends TestCase {

    public void testSum() {
        int a = 5;
        int b = 10;
        int result = a + b;
        assertEquals(15, result);
    }
}

Підхід B (JUnit 4-стиль): створити "нормальний" клас та додати @Testпримітку до методу. Зауважте, що НЕ потрібно починати метод зі слова test.

import org.junit.*;
import static org.junit.Assert.*;

public class DummyTestB {

    @Test
    public void Sum() {
        int a = 5;
        int b = 10;
        int result = a + b;
        assertEquals(15, result);
    }
}

Змішування двох, здається, не є гарною ідеєю, див. Наприклад, це питання про stackoverflow :

Тепер мої питання:

  1. Що є кращим підходом , або коли ви використовуєте один замість іншого?
  2. Підхід B дозволяє перевірити винятки шляхом розширення анотації @Test, як у @Test(expected = ArithmeticException.class). Але як ви перевіряєте винятки при використанні підходу A?
  3. Використовуючи підхід А, ви можете згрупувати ряд тестових класів у тестовому наборі, як це:

    TestSuite suite = new TestSuite("All tests");
    suite.addTestSuite(DummyTestA.class);
    suite.addTestSuite(DummyTestAbis.class);

    Але це не може бути використане з підходом B (оскільки кожен тестовий клас повинен підкласировать TestCase). Який правильний спосіб групувати тести для підходу B?

Редагувати: Я додав версії JUnit до обох підходів


Я бачив, extends TestCaseа потім кожен тест також зазначався, @Testщоб просто заплутати речі. :)
EM-Creations

Відповіді:


119

Розрізнити досить легко:

  • розширення TestCase- це те, як були написані одиничні тести в JUnit 3 (звичайно, це все ще підтримується в JUnit 4)
  • використання @Testанотації - це спосіб, представлений JUnit 4

Як правило, слід вибрати шлях анотації, якщо не потрібна сумісність з JUnit 3 (та / або версією Java раніше Java 5). Новий спосіб має ряд переваг:

  • @TestAnnotaton чіткіше і легше підтримки в інструментах (наприклад, легко знайти всі тести таким чином)
  • Різні методи можуть бути анотований з @Before/ @BeforeClassі @After/ @AfterClassзабезпечуючи більшу гнучкість
  • Підтримка @Ruleприміток про такі речіExpectedException
  • Підтримка @Ignoredпримітки
  • Підтримка альтернативних тестових бігунів, що використовують @RunWith

Щоб перевірити очікувані винятки в JUnit 3, TestCaseвам слід зробити текст явним.

public void testMyException() {
  try {
    objectUnderTest.myMethod(EVIL_ARGUMENT);
    fail("myMethod did not throw an Exception!");
  } catch (MyException e) {
    // ok!
    // check for properties of exception here, if desired
  }
}

JUnit 5 представив ще одну зміну API, але все ще використовує анотації. Нова @Testанотація є org.junit.jupiter.api.Test("стара" JUnit 4 була org.junit.Test), але вона працює майже так само, як і JUnit 4.


Корисна і ретельна відповідь, але я не повністю розумію "перевірка на повідомлення про виняток". Перевірка жорсткого коду буде кошмаром технічного обслуговування. Ви, мабуть, мали на увазі "перевірити властивості конкретного типу виключення".
thSoft

3
@thSoft: не часто він використовується, але час від часу я хочу переконатися, що, наприклад, метод виключення згадує поле порушника. Тоді простий assertTrue(e.getMessage().contains("foo"))може бути корисним.
Йоахім Зауер

1
Навіть у JUnit4 це важлива ідіома, коли вам потрібно перевірити повідомлення чи якесь інше властивість виключення (наприклад, причину). expectedМетод перевіряє тільки тип.
Yishai

@Yishai: це правда, але більшість часу я вже задоволений, якщо метод викидає правильний тип винятку при проблемному введенні.
Йоахім Зауер

З цієї причини JUnit 5 змінив тестування виключень. assertThrows () є фантастичним :-)
Маркус К.

25

Я маю перевагу щодо JUnit 4 (Анотаційний підхід), оскільки вважаю його більш гнучким.

Якщо ви хочете створити тестовий набір у JUnit 4, вам слід створити клас, що групує всі тести, як це:

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


@RunWith(Suite.class)
@SuiteClasses({
    Test1.class,
    Test2.class,
    Test3.class,
    Test4.class
})public class TestSuite
{
 /* empty class */
}

15

На ваше запитання є відповідь, і це "Який спосіб групувати тести для підходу B?"

Офіційна відповідь полягає в тому, що ви коментуєте клас за допомогою @RunWith (Suite.class), а потім використовуєте примітку @ Suite.SuiteClasses, щоб перелічити класи. Так це роблять розробники JUnit (перелічуючи кожен клас в наборі вручну). Багато в чому цей підхід є вдосконаленням, оскільки тривіально та інтуїтивно додавати перед набором і після набору поведінки (просто додайте метод класу @BeforeClass та @AfterClass до класу, анотований за допомогою @RunWith - набагато краще, ніж старий TestFixture ).

Однак у нього є крок назад, оскільки анотації не дозволяють вам динамічно створювати список класів, і обробка цієї проблеми стає трохи негарною. Вам потрібно підкласифікувати клас Suite та динамічно створювати масив класів у підкласі та передавати його конструктору Suite, але це неповне рішення у тому, що інші підкласи Suite (наприклад, категорії) не працюють з ним, а по суті не підтримують динамічну колекцію тестових класів.


1
+1 для цього. Після вирішення завдання написати динамічне рішення для додавання тестів до TestSuite, мені довелося розширити TestCase у кожному з моїх тестів. Це, у свою чергу, порушило раніше тести робочого блоку, які використовували анотації JUnit4 для визначення очікуваних винятків. Мій пошук способу динамічного заповнення тестового набору привів мене до цієї теми, і конкретно ваша відповідь, яка, на мою думку, є однією з небагатьох бажаних причин продовжувати роботу з JUnit 3.
8bitjunkie

4

Слід використовувати JUnit 4. Краще.

Підтримка JUnit 3.8 почала відміняти багато структур.

Це з довідкової документації Spring 3.0:

[Попередження] Спадщина ієрархії класів JUnit 3.8 застаріла

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


1
  1. "Кращим" підходом було б використання анотацій, які були запроваджені з 4 червня. Вони багато чого полегшують (див. Друге запитання)

  2. Для цього можна скористатися простим блоком спробувати / ловити:


public void testForException() {
    try {
        Integer.parseInt("just a string");
        fail("Exception should have been thrown");
    } catch (final Exception e) {
        // expected
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.