Как запустить все тесты, принадлежащие к определенной категории в JUnit 4 - PullRequest
69 голосов
/ 01 февраля 2010

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

В примечаниях к выпуску 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 ...

Ответы [ 6 ]

59 голосов
/ 01 февраля 2010

Я нашел один из возможных способов достижения того, чего я хочу, но я не считаю это лучшим решением, поскольку оно опирается на библиотеку 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 проекта здесь. Он найдет все классы с тестами.

7 голосов
/ 13 июня 2011

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

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

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

5 голосов
/ 12 января 2013

Недостатком решения 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 к аргументам виртуальной машины. (Обратите внимание, что это решение ссылается на быстрый набор тестов вместо медленного.)

2 голосов
/ 10 ноября 2012

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

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

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

}
1 голос
/ 01 февраля 2010

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

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

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

0 голосов
/ 01 февраля 2010

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

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

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

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

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