Как определить правило метода JUnit в наборе тестов? - PullRequest
10 голосов
/ 03 октября 2011

У меня есть класс, который представляет собой набор тестовых классов JUnit. Я хотел бы определить правило для suite , чтобы что-то делать с базой данных до и после каждого модульного теста , если для этого имеется определенная аннотация метод испытания.

Мне удалось создать @ClassRule в наборах и тестовых классах, которые будут делать это перед каждым классом (что недостаточно хорошо), и я смог определить тест правила с самими тестовыми классами, но это повторяется и выглядит не очень СУХОЙ.

Можно ли определить правило для каждого метода тестирования в комплекте или я должен добавить их к каждому тесту?

Редактировать: Для пояснения я хочу объявить код в наборе, который будет выполняться между (то есть "вокруг") методами тестирования в классах тестирования.

Ответы [ 5 ]

14 голосов
/ 05 октября 2011

Это можно сделать, но для этого нужно немного поработать. Вам также нужно определить свой собственный Runner Suite и собственный Runner Test, а затем переопределить runChild () в runner теста. Использование следующих классов Suite и Test:

@RunWith(MySuite.class)
@SuiteClasses({Class1Test.class})
public class AllTests {
}

public class Class1Test {
    @Deprecated @Test public void test1() {
        System.out.println("" + this.getClass().getName() + " test1");
    }

    @Test public void test2() {
        System.out.println("" + this.getClass().getName() + " test2");
    }
}

Обратите внимание, что я пометил test1() с @Deprecated. Вы хотите сделать что-то другое, когда у вас есть аннотация @Deprecated в тесте, поэтому нам нужно расширить Suite, чтобы использовать пользовательскую Runner:

public class MySuite extends Suite {
    // copied from Suite
    private static Class<?>[] getAnnotatedClasses(Class<?> klass) throws InitializationError {
        Suite.SuiteClasses annotation = klass.getAnnotation(Suite.SuiteClasses.class);
        if (annotation == null) {
            throw new InitializationError(String.format("class '%s' must have a SuiteClasses annotation", klass.getName()));
        }
        return annotation.value();
    }

    // copied from Suite
    public MySuite(Class<?> klass, RunnerBuilder builder) throws InitializationError {
        super(null, getRunners(getAnnotatedClasses(klass)));
    }

    public static List<Runner> getRunners(Class<?>[] classes) throws InitializationError {
        List<Runner> runners = new LinkedList<Runner>();

        for (Class<?> klazz : classes) {
            runners.add(new MyRunner(klazz));
        }

        return runners;
    }
}

JUnit создает Runner для каждого запускаемого теста. Обычно Suite просто создает значение по умолчанию BlockJUnit4ClassRunner, все, что мы здесь делаем, - это перезаписываем конструктор для Suite, который читает классы из аннотации SuiteClass, и мы создаем с ними наши собственные бегуны MyRunner. Это наш класс MyRunner:

public class MyRunner extends BlockJUnit4ClassRunner {
    public MyRunner(Class<?> klass) throws InitializationError {
        super(klass);
    }

    @Override
    protected void runChild(final FrameworkMethod method, RunNotifier notifier) {
        Description description= describeChild(method);
        if (method.getAnnotation(Ignore.class) != null) {
            notifier.fireTestIgnored(description);
        } else {
            if (description.getAnnotation(Deprecated.class) != null) {
                System.out.println("name=" + description.getMethodName() + " annotations=" + description.getAnnotations());
            }
            runLeaf(methodBlock(method), description, notifier);
        }
    }
}

Большая часть этого скопирована с BlockJUnit4ClassRunner. Я добавил бит:

if (description.getAnnotation(Deprecated.class) != null) {
    System.out.println("name=" + description.getMethodName() + " annotations=" + description.getAnnotations());
}

, где мы проверяем наличие аннотации @Deprecated в методе и делаем что-то, если оно там есть. Остальное оставлено в качестве упражнения для читателя. Когда я запускаю вышеуказанный пакет, я получаю в качестве вывода:

name=test1 annotations=[@java.lang.Deprecated(), @org.junit.Test(expected=class org.junit.Test$None, timeout=0)]
uk.co.farwell.junit.run.Class1Test test1
uk.co.farwell.junit.run.Class1Test test2

Обратите внимание, что Suite имеет несколько конструкторов в зависимости от того, как он вызывается. Вышеуказанное работает с Eclipse, но я не тестировал другие способы запуска Suite. См. Комментарии вместе с различными конструкторами для Suite для получения дополнительной информации.

2 голосов
/ 04 октября 2011

Вы можете использовать RunListener , который вы добавляете в Suite.Он не дает вам всего, что может сделать правило, но предоставляет класс Description, в котором должны быть доступны аннотации.По крайней мере, я не думаю, что JUnit фильтрует его только по понятным аннотациям.

Разработчик JUnit только что обсудил механизм добавления RunListener в Suite здесь .

1 голос
/ 04 октября 2011

Само по себе добавление правила к классу, помеченному @RunWith(Suite.class), не поможет.Я полагаю, что это потому, что Suite - это ParentRunner<Runner>, а не Runner, такой как BlockJUnit4ClassRunner, который попытается очистить правила для классов, которые он запускает.Чтобы запустить своих детей, он говорит ребенку Runners бежать.Эти Runner s, возможно, создали свои тесты, применяя правила к этим классам, но бегун Suite не предпринимает никаких специальных действий, чтобы применить собственные правила к тестам своей дочерней Runner s сборки.

0 голосов
/ 17 декабря 2013

Вы пытались использовать «иерархию тестовых классов»?Я часто использую абстрактный класс теста, чтобы поделиться тестом или приспособлением.Например, все мои тесты БД инициализируют встроенный источник данных.Сначала я создаю абстрактный класс "DbTestCase", который обрабатывает логику инициализации.Тогда весь подкласс пойдет на пользу тесту и приборам.

Однако иногда я сталкиваюсь с проблемой, когда в моих тестовых примерах требуется много логики test / fixture, которую я не могу сохранить в одной иерархии.В этом случае только аспектное программирование решает проблему.Маркировка логики тестирования / фикстуры с помощью специальных аннотаций / интерфейсов, которые может реализовать любой требуемый класс.

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

0 голосов
/ 04 октября 2011

Вы можете групповые тесты с TestNG.И вы можете настроить TestNG для запуска некоторой логики @ BeforeGroup и @ AfterGroup .

...