JUnit путаница: использовать «extends TestCase» или «@Test»?

152

Я нашел правильное использование (или, по крайней мере, документацию) JUnit очень запутанным. Этот вопрос служит как будущей ссылкой, так и реальным вопросом.

Если я правильно понял, есть два основных подхода к созданию и запуску теста JUnit:

Подход A (стиль JUnit 3): создайте класс, расширяющий 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). Но как вы проверяете исключения при использовании подхода А?
  3. При использовании подхода A вы можете сгруппировать несколько классов тестов в набор тестов следующим образом:

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

    Но это не может быть использовано с подходом B (поскольку каждый тестовый класс должен быть подклассом TestCase). Как правильно группировать тесты для подхода B?

Изменить: я добавил версии JUnit для обоих подходов

Rabarberski
источник
Я видел, 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: это правда, но большую часть времени я уже доволен, если метод выдает правильный тип исключения при проблемном вводе.
Иоахим Зауэр
По этой причине в 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 */
}
Grimmy
источник
15

В вашем вопросе есть неотвеченная часть, а именно: «Как правильно группировать тесты для подхода B»?

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

Тем не менее, он имеет шаг назад, поскольку аннотации не позволяют динамически создавать список классов, и решение этой проблемы становится немного уродливым. Вы должны создать подкласс класса Suite и динамически создать массив классов в подклассе и передать его конструктору Suite, но это неполное решение в том смысле, что другие подклассы Suite (такие как Categories) не работают с ним и по существу не поддерживают динамический набор классов Test.

Ишай
источник
1
+1 за это. После начала задачи по написанию динамического решения для добавления тестов в TestSuite мне пришлось расширять TestCase в каждом из моих тестов. Это, в свою очередь, нарушило ранее работающие юнит-тесты, которые использовали аннотации JUnit4 для определения ожидаемых исключений. Мой поиск способа динамического заполнения Test Suite привел меня к этой теме, а именно к вашему ответу, который, я считаю, является одной из немногих оставшихся желательных причин для продолжения работы с JUnit 3.
8bitjunkie
4

Вы должны использовать JUnit 4. Это лучше.

Многие фреймворки начали осуждать поддержку JUnit 3.8.

Это из справочной документации Spring 3.0:

[Предупреждение] Устаревшая иерархия классов JUnit 3.8 устарела

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

Эспен
источник
1
  1. «Предпочтительным» подходом будет использование аннотаций, которые были введены начиная с Junit 4. Они облегчают многие вещи (см. Ваш второй вопрос)

  2. Для этого вы можете использовать простой блок try / catch:


public void testForException() {
    try {
        Integer.parseInt("just a string");
        fail("Exception should have been thrown");
    } catch (final Exception e) {
        // expected
    }
}
black666
источник