2010-02-01 1 views
66

JUnit 4.8 содержит новую новую функцию под названием «Категории», которая позволяет группировать определенные типы тестов вместе. Это очень полезно, например. иметь отдельные тестовые прогоны для медленных и быстрых тестов. Я знаю материал, упомянутый в JUnit 4.8 release notes, но хотел бы знать, как я могу фактически запустить все тесты, аннотированные определенной категорией.Как выполнить все тесты, относящиеся к определенной категории в JUnit 4

В JUnit 4.8 Замечания к выпуску показывает определение примера ванной, где SuiteClasses аннотация выбирает тесты из определенной категории, чтобы бежать, как это:

@RunWith(Categories.class) 
@IncludeCategory(SlowTests.class) 
@SuiteClasses({ A.class, B.class }) // Note that Categories is a kind of Suite 
public class SlowTestSuite { 
    // Will run A.b and B.c, but not A.a 
} 

Кто-нибудь знает, как я могу запустить все тесты в категории SlowTests ? Кажется, что вы должны иметь аннотацию SuiteClasses ...

+1

Привет. У меня есть вопрос, который связан. не стесняйтесь звонить: http://stackoverflow.com/questions/15776718/using-junit-categories-vs-simply-organizing-logical-test-categories-in-selectate – amphibient

ответ

59

Я узнал один из возможных способов достижения того, чего хочу, но я не считаю это лучшим решением, поскольку он полагается на библиотеку ClassPathSuite, которая не является частью JUnit.

Я определяю набор тестов для медленных тестов, как это:

@RunWith(Categories.class) 
@Categories.IncludeCategory(SlowTests.class) 
@Suite.SuiteClasses({ AllTests.class }) 
public class SlowTestSuite { 
} 

Alltests класс определяется следующим образом:

@RunWith(ClasspathSuite.class) 
public class AllTests { 
} 

Я должен был использовать класс ClassPathSuite из ClassPathSuite проекта здесь. Он найдет все классы с тестами.

+4

Это действительно разумное решение. Спасибо за то, что вы задали свой собственный вопрос, потому что он действительно хороший :-) –

+0

Для кого-то интересно, как автоматизировать запуск категории тестов (с помощью этой точной настройки) с помощью Ant, [этот вопрос] (http://stackoverflow.com/questions/6226026/how-to-run-all-junit-tests-in-a-category-suite-with-ant) может оказаться полезным. – Jonik

+3

Как комплимент моему вопросу http://stackoverflow.com/q/2698174/59470 и подробное объяснение Я добавил запись в блоге: http://novyden.blogspot.com/2011/06/using-junit-4-categories -заменить.html – topchef

1

Я не уверен, что именно ваша проблема.

Просто добавьте все тесты в набор (или хирачи люксов). Затем используйте аннотацию категории Runner и Include/ExcludeCategory, чтобы указать категории, которые вы хотите запустить.

Хорошей идеей может быть наличие одного набора, содержащего все тесты, и несколько отдельных наборов, относящихся к первому, с указанием различных категорий категорий, которые вы указали.

+19

Моя проблема в том, что у меня есть тысячи тестов и Я не хочу вручную добавлять их в любые сюиты. Я просто хочу, чтобы тесты с определенной категорией выполнялись. Для JUnit не должно быть так сложно выяснить, какие тесты имеют определенную аннотацию, поскольку она на самом деле это делает при поиске методов тестирования. – Kaitsu

1

не прямой ответ на вашу проблему, но, возможно, общий подход может быть улучшен ...

Почему ваши тесты медленно? Возможно, настройка длится долго (база данных, ввод-вывод и т. Д.), Возможно, тесты слишком много тестируются? Если это так, я бы отделил настоящие модульные тесты от «длинных», которые часто действительно являются интеграционными тестами.

В моих установках у меня есть очередь env, где часто выполняются модульные тесты и интеграционные тесты, но реже (например, после каждой фиксации в управлении версиями). Я никогда не работал с группировкой для модульных тестов, потому что они должны быть свободно связаны друг с другом. Я работаю только с группировкой и отношениями тестовых случаев в установках тестирования интеграции (но с TestNG).

Но хорошо знать, что JUnit 4.8 представил некоторые функции группировки.

+1

Спасибо Мануэлю за ваши комментарии! Мне не нужно разделять модульные тесты, но я использую JUnit также для тестов интеграции и хочу отделить их от модульных тестов. Я также посмотрел на TestNG и, похоже, сделал тестирование (а не только модульное тестирование) более приятным, чем JUnit. И у него также есть хорошая документация и хорошая книга. – Kaitsu

7

Вот некоторые из основных различий между TestNG и JUnit, когда речь идет о группах (или категории, как JUnit называет их):

  • JUnit являются напечатал (аннотаций), тогда как в TestNG являются строками. Я сделал этот выбор, потому что я хотел иметь возможность использовать регулярные выражения при запуске тестов, например «запустить все тесты, принадлежащие к базе данных группы» *.Кроме того, необходимость создавать новую аннотацию всякий раз, когда вам нужно создать новую категорию, вызывает раздражение, хотя она имеет то преимущество, что IDE сразу сообщит вам, где эта категория используется (TestNG показывает это в своих отчетах).

  • TestNG отлично отделяет вашу статическую модель (код ваших тестов) от модели времени выполнения (какие тесты запускаются). Если вы хотите сначала запустить группы «front-end», а затем «сервлеты», вы можете сделать это, не перекомпилируя ничего. Поскольку JUnit определяет группы в аннотации, и вам нужно указать эти категории в качестве параметров для бегуна, вам обычно приходится перекомпилировать свой код, когда вы хотите запустить другой набор категорий, что наносит мне ущерб, на мой взгляд.

+0

Мы построили нашу собственную поддержку категорий в наших тестах JUnit очень похожим образом на JUnit, основное отличие заключается в том, что вместо аннотации @ Categories.IncludeCategory мы сделали нашу конфигурацию через системное свойство. Почему это было слишком сложно для Юнита, чтобы сделать для нас, - это все догадывается. – Trejkaz

2

Для запуска категоризированных тестов без указания их всех explicily в @Suite.SuiteClasses аннотаций вы можете предоставить свою собственную реализацию Suite. Например, org.junit.runners.ParentRunner может быть продлен. Вместо использования массива классов, предоставляемого @Suite.SuiteClasses, новая реализация должна выполнять поиск категоризированных тестов в пути к классам.

См. this project в качестве примера такого подхода. Использование:

@Categories(categoryClasses = {IntegrationTest.class, SlowTest.class}) 
@BasePackage(name = "some.package") 
@RunWith(CategorizedSuite.class) 
public class CategorizedSuiteWithSpecifiedPackage { 

} 
5

Один недостаток решения Kaitsu является то, что Eclipse будет запускать тесты дважды, и SlowTests 3 раза, при выполнении всех тестов в проекте. Это связано с тем, что Eclipse будет запускать все тесты, а затем пакет AllTests, затем SlowTestSuite.

Это решение, которое предполагает создание подклассов тестовых бегущих решений Kaitsu для пропуска номеров, если не задано определенное системное свойство. Позорный взлом, но все, что я придумал до сих пор.

@RunWith(DevFilterClasspathSuite.class) 
public class AllTests {} 

.

@RunWith(DevFilterCategories.class) 
@ExcludeCategory(SlowTest.class) 
@SuiteClasses(AllTests.class) 
public class FastTestSuite 
{ 
} 

.

public class DevFilterCategories extends Suite 
{ 
    private static final Logger logger = Logger 
     .getLogger(DevFilterCategories.class.getName()); 
    public DevFilterCategories(Class<?> suiteClass, RunnerBuilder builder) throws InitializationError { 
     super(suiteClass, builder); 
     try { 
      filter(new CategoryFilter(getIncludedCategory(suiteClass), 
        getExcludedCategory(suiteClass))); 
      filter(new DevFilter()); 
     } catch (NoTestsRemainException e) { 
      logger.info("skipped all tests"); 
     } 
     assertNoCategorizedDescendentsOfUncategorizeableParents(getDescription()); 
    } 

    private Class<?> getIncludedCategory(Class<?> klass) { 
     IncludeCategory annotation= klass.getAnnotation(IncludeCategory.class); 
     return annotation == null ? null : annotation.value(); 
    } 

    private Class<?> getExcludedCategory(Class<?> klass) { 
     ExcludeCategory annotation= klass.getAnnotation(ExcludeCategory.class); 
     return annotation == null ? null : annotation.value(); 
    } 

    private void assertNoCategorizedDescendentsOfUncategorizeableParents(Description description) throws InitializationError { 
     if (!canHaveCategorizedChildren(description)) 
      assertNoDescendantsHaveCategoryAnnotations(description); 
     for (Description each : description.getChildren()) 
      assertNoCategorizedDescendentsOfUncategorizeableParents(each); 
    } 

    private void assertNoDescendantsHaveCategoryAnnotations(Description description) throws InitializationError {   
     for (Description each : description.getChildren()) { 
      if (each.getAnnotation(Category.class) != null) 
       throw new InitializationError("Category annotations on Parameterized classes are not supported on individual methods."); 
      assertNoDescendantsHaveCategoryAnnotations(each); 
     } 
    } 

    // If children have names like [0], our current magical category code can't determine their 
    // parentage. 
    private static boolean canHaveCategorizedChildren(Description description) { 
     for (Description each : description.getChildren()) 
      if (each.getTestClass() == null) 
       return false; 
     return true; 
    } 
} 

.

public class DevFilterClasspathSuite extends ClasspathSuite 
{ 
    private static final Logger logger = Logger 
     .getLogger(DevFilterClasspathSuite.class.getName()); 
    public DevFilterClasspathSuite(Class<?> suiteClass, RunnerBuilder builder) 
     throws InitializationError { 
     super(suiteClass, builder); 
     try 
     { 
      filter(new DevFilter()); 
     } catch (NoTestsRemainException e) 
     { 
      logger.info("skipped all tests"); 
     } 
    } 
} 

.

public class DevFilter extends Filter 
{ 
    private static final String RUN_DEV_UNIT_TESTS = "run.dev.unit.tests"; 

    @Override 
    public boolean shouldRun(Description description) 
    { 
     return Boolean.getBoolean(RUN_DEV_UNIT_TESTS); 
    } 

    @Override 
    public String describe() 
    { 
     return "filter if "+RUN_DEV_UNIT_TESTS+" system property not present"; 
    } 
} 

Таким образом, в вашем FastTestSuite пусковая, просто добавьте -Drun.dev.unit.tests = True к аргументам VM. (Обратите внимание, что это решение относится к быстрому набору тестов вместо медленного.)

Смежные вопросы