Статьи

Пристальный взгляд на JUnit Категории

В JUnit 4.8 введены категории : механизм для маркировки и группирования тестов, предоставляющий разработчикам возможность включать или исключать группы (или категории). В этом посте представлен краткий обзор категорий JUnit и некоторых неожиданных действий, которые я обнаружил при их использовании.

1. Краткое введение

В следующем примере показано, как использовать категории: (адаптировано из заметок о выпуске JUnit)

public interface FastTests { /* category marker */ }
public interface SlowTests { /* category marker */ }

public class A {
@Category(SlowTests.class)
@Test public void a() {}
}

@Category(FastTests.class})
public class B {
@Test public void b() {}
}

@RunWith(Categories.class)
@IncludeCategory(SlowTests.class)
@ExcludeCategory(FastTests.class)
@SuiteClasses({ A.class, B.class })
public class SlowTestSuite {}

Строки 1, 2: мы определяем две категории, FastTests и SlowTests. Категории JUnit могут быть определены как классы или интерфейсы. Поскольку категория действует как метка или маркер, моя интуиция подсказывает мне использовать интерфейсы.

Строка 5: мы используем аннотацию @ org.junit.experimental.categories.Category для обозначения тестовых классов и методов тестирования одной или несколькими категориями.

Строки 6, 9: методы испытаний и классы испытаний могут быть помечены как принадлежащие к одной или нескольким категориям испытаний. Маркировка тестового класса категорией автоматически включает все его методы тестирования в этой категории.

Строки с 14 по 18: в настоящее время программные тестовые наборы (строка 17) являются единственным способом указать, какие категории тестов (строка 14) следует включать (строка 15) или исключать (строка 16) при выполнении набора. Я считаю этот подход (особенно способ, которым тестовые классы должны быть включены в комплект) слишком многословным и не таким гибким. Надеемся, что Ant, Maven и IDE обеспечат поддержку категорий (с более простой конфигурацией) в самое ближайшее время.

Примечание: я недавно обнаружил ClasspathSuite , проект, который упрощает создание программных наборов тестов JUnit. Например, мы можем указать, что хотим включить в набор тестов все тесты, имена которых заканчиваются на «UnitTest».

2. Подтип категории

Категории также поддерживают подтипы. Допустим, у нас есть категория IntegrationTests, которая расширяет SlowTests:

public interface IntegrationTests extends SlowTests {} 

Любой класс теста или метод теста, помеченный категорией IntegrationTests, также является частью категории SlowTests. Честно говоря, я не знаю, насколько удобным может быть подтип категории. Мне нужно больше экспериментировать с этим, чтобы иметь мнение.

3. Категории и наследование тестов

3a. Категории уровня метода

JUnit ведет себя так, как ожидается, когда наследование теста объединяется с категориями уровня метода. Например:

public class D {
@Category(GuiTest.class)
@Test public void d() {}
}
public class E extends D {
@Category(GuiTest.class)
@Test public void e() {}
}
@RunWith(Categories.class)
@IncludeCategory(GuiTest.class)
@SuiteClasses(E.class)
public class TestSuite {} 

Как и ожидалось, при запуске TestSuite выполняются тестовые методы d и e (оба метода относятся к категории GuiTest, а E наследует метод d от суперкласса D.) Отлично!

3b. Категории уровня класса

С другой стороны, если я что-то упустил, я думаю, что обнаружил странное поведение в JUnit в этом сценарии. Рассмотрим следующие классы:

@Category(GuiTest.class)
public class A {
@Test public void a() {}
}
public class B extends A {
  @Test public void b() {}
}
@RunWith(Categories.class)
@IncludeCategory(GuiTest.class)
@SuiteClasses(B.class)
public class TestSuite {}

Как мы видим, TestSuite должен выполнить тесты в B, которые принадлежат категории GuiTest. Я ожидал, что TestSuite выполнит тестовый метод a, хотя B не помечен как GuiTest. Вот мои рассуждения:

  1. Тестовый метод относится к категории GuiTest, потому что тестовый класс A помечен такой категорией
  2. Тестовый класс B является A, и он наследует тестовый метод A

Поэтому TestSuite должен выполнить тестовый метод a. Но это не так! Вот скриншот результатов, которые я получаю (нажмите, чтобы увидеть в полном размере.)

Есть два способа исправить эту проблему, в зависимости от того, какие методы тестирования мы хотим запустить:

  1. Метка класса B с GuiTest. В этом случае оба метода, a и b, будут выполнены.
  2. Метка А с помощью GuiTest. В этом случае будет выполняться только метод a.

(Я скоро опубликую вопрос по этой проблеме в списке рассылки JUnit.)

4. Категории против групп TestNG

(Вы видели, что это идет, не так ли?) Категории (или группы) были частью TestNG в течение долгого времени. В отличие от JUnit, группы TestNG определяются как простые строки, а не как классы или интерфейсы.

Как любитель статической типизации, я был очень доволен категориями JUnit. Используя IDE, мы можем смело переименовывать категорию или искать использование категории в проекте. Хотя мои наблюдения были верны, я упустил один важный момент: все это прекрасно работает, пока ваш набор тестов написан на Java.

В реальном мире я бы хотел определить набор тестов либо в Ant, либо в Maven (или в Gradle, либо в Rake.) В этом сценарии наличие категорий в качестве типов Java не приносит никакой пользы. На самом деле, я подозреваю, что было бы очень многословно и подвержено ошибкам указывать полное имя категории в скрипте сборки. Переименование категории теперь будет ограничено текстовым «поиск и замена». Ant и Maven действительно должны предоставить способ указания категорий JUnit, достаточно умный, чтобы быть надежным.

Как вы можете ожидать, я предпочитаю простоту и прагматизм групп TestNG.

Обновление: мой хороший друг (и создатель среды TestNG) Седрик напомнил мне, что мы можем использовать регулярные выражения для включения или исключения групп в наборе тестов (подробности здесь ). Это действительно мощно!

5. Мое использование категорий

Я пока не использую категории JUnit в моих тестовых пакетах. Я начал изучать категории JUnit, потому что не был полностью доволен тем, как мы распознавали тесты GUI в FEST . Мы распознаем методы тестирования или тестовые классы как «тесты GUI», если они были аннотированы с помощью @GUITest (предоставлено FEST.) Когда «тест GUI» не проходит, FEST автоматически делает снимок экрана рабочего стола и включает его в JUnit или TestNG HTML отчет. Проблема в том, что наша аннотация @GUITest дублирует функциональность категорий JUnit.

Чтобы решить эту проблему, я создал расширение JUnit, которое распознает методы тестирования или тестовые классы как «тесты GUI», если они относятся к категории GuiTest. На данный момент GuiTest является интерфейсом, предоставленным FEST, но я думаю о том, чтобы позволить пользователям также указывать свою собственную категорию GuiTest.

Я также реорганизовал эту функциональность из модуля тестирования Swing, ожидая его повторного использования, как только я реализую модуль тестирования JavaFX ?

Вы можете найти код FEST, относящийся к категориям JUnit, на github .

6. Заключение

Возможность помечать и группировать тесты по категориям — это действительно отличная возможность. У меня все еще есть некоторые сомнения относительно целесообразности определения категорий как типов Java, отсутствия поддержки этой функции в Ant и Maven (не по вине JUnit) и неожиданного поведения, которое я заметил при объединении категорий уровня класса и наследования тестов.

С другой стороны, категории все еще являются экспериментальной, не финальной функцией. Я уверен, что увидим много улучшений в будущих выпусках JUnit ?

Обратная связь всегда приветствуется.

С http://alexruiz.developerblogs.com/?p=1711