2012-06-22 3 views
16

Я использую jUnit для управления интеграционными тестами для приложения, которое обращается к базе данных. Поскольку настройка тестовых данных - это трудоемкая операция, я делаю это в методе @BeforeClass, который выполняется только один раз для каждого тестового класса (в отличие от метода @Before, который запускается один раз для каждого тестового метода).С jUnit 4 можно ли параметризовать @BeforeClass?

Теперь я хочу попробовать несколько разных перестановок для настройки уровня данных, выполнив все мои тесты для каждой другой конфигурации. Это кажется естественным использованием тестового бегуна Parameterized. Проблема заключается в том, что Parameterized предоставляет параметры конструктору класса, а метод @BeforeClass является абстрактным и вызывается перед конструктором класса.

Несколько вопросов,

ли Parameterized вызов метода @BeforeClass для каждой перестановки параметров, или же это только позвонить один раз?

Если метод @BeforeClass вызывается повторно, есть ли способ получить доступ к значениям параметров изнутри?

Если ни один из них, что люди считают лучшим альтернативным подходом к этой проблеме?

+0

Посмотрите, может ли http://code.google.com/p/junitparams/ помочь – Jayan

+0

Так что до сих пор нет способа сделать это? –

+1

Эффект может быть выполнен с помощью специального тестового бегуна. Как правило, вы должны подклассифицировать BlockJUnit4ClassRunner. –

ответ

1

@BeforeClass вызывается только один раз в вашем примере. Что имеет смысл, учитывая имя - перед классом!

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

  1. Настройки, что данные в @Before так специфическое испытание
  2. Группы тестов, которые вы хотите работать с те же данные в отдельные классы тестов и использовать @BeforeClass для каждого из них.
+1

Благодарим вас за разъяснение моего понимания @ BeforeClass. К сожалению, два подхода, которые вы наброски, не решают мою большую проблему. Проблема с подходом 1 заключается в том, что у меня есть дорогостоящая одноразовая инициализация, которая меняется от одного сценария к другому, но я не хочу повторять для каждого теста. Проблема с подходом 2 заключается в том, что мне пришлось бы писать другой тестовый класс для каждого сценария, хотя я выполняю те же тесты. –

+1

Дэн, для опции № 2 вы можете подкласса. Проведите «те же тесты» в суперклассе, а затем каждый подкласс отражает сценарий. Другими словами, подклассы просто содержат @BeforeClass –

+0

Этот подход будет технически работать, но я не считаю его привлекательным, так как у меня уже есть десятки тестовых классов, и в конечном итоге ожидается, что сотни –

-1

Вы можете выполнить инициализацию в методе @Before, записывая переменную экземпляра, но проверяя значение null.

@RunWith(value = Parameterized.class) 
public class BigThingTests { 
    private BigThing bigThing; 

    @Before 
    public void createBitThing() { 
    if (bigThing == null) { 
     bigThing = new BigThing(); 
    } 
    } 

... 
} 

Новый экземпляр BigThingTests создается для каждого набора параметров, и bigThing устанавливается нулевое значение с каждым новым экземпляром. Бегун Parameterized является однопоточным, поэтому вам не нужно беспокоиться о нескольких инициализациях.

+0

this.bigThing всегда будут иметь значение null, так как вы получаете новый экземпляр BigThingTests для каждого @Test. –

0

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

Я не могу придумать эквивалент для AfterClass.

+1

Использование конструктора вместо '@ Before' не помогает, поскольку стандартный тестовый бегун JUnit 4 создает новый экземпляр тестового класса для каждого тестового метода. Таким образом, у меня все еще будет проблема, что мой дорогостоящий код установки вызывается один раз для каждого метода и перестановки писем, а не только для каждой перестановки параметров. –

+0

Хороший улов. Я обновил свой ответ (и тесты, над которыми я работаю с той же проблемой). – TREE

3

Думаю, вам понадобится пользовательский тестовый бегун. У меня та же проблема, с которой вы сталкиваетесь (нужно запускать те же тесты с использованием нескольких дорогостоящих конфигураций). Вам понадобится способ параметризации настройки, возможно, используя аннотации @Parameter, аналогичные тем, которые используются параметризуемым бегуном, но по статическим полям-членам вместо полей экземпляра. Пользовательский бегун должен будет найти все статические поля-члены с аннотацией @Parameter, а затем запустить тестовый класс (возможно, используя базовый BlockJunit4ClassRunner) один раз в статическом поле @Parameter. Поле @Parameter должно быть @ClassRule.

Andy on Software отлично поработала над разработкой пользовательских тестовых бегунов, и он настолько четко объясняет эти сообщения в блоге here и here.

+0

+1 для @ClassRule, который отлично работает, не мешая параметризованной функциональности или требуя пользовательского бегуна – bstoney

0

Это старый вопрос, но мне просто пришлось решить, вероятно, подобную проблему. На данный момент я пошел с решением ниже, которое по существу представляет собой реализацию ответа TREE (обновленного) с использованием общего абстрактного базового класса, чтобы избежать дублирования всякий раз, когда вам нужен этот механизм.

Испытания на бетонные конструкции предоставят метод @Parameters, который возвратит итерабельность одноэлементных массивов, содержащих Поставщика < T> каждый. Затем эти поставщики выполняются ровно один раз за фактический ввод, необходимый для конкретных методов испытаний.

@RunWith(Parameterized.class) 
public class AbstractBufferedInputTest<T> { 

private static Object INPUT_BUFFER; 

private static Object PROVIDER_OF_BUFFERED_INPUT; 

private T currentInput; 

@SuppressWarnings("unchecked") 
public AbstractBufferedInputTest(Supplier<T> inputSuppler) { 
    if (PROVIDER_OF_BUFFERED_INPUT != inputSuppler) { 
     INPUT_BUFFER = inputSuppler.get(); 
     PROVIDER_OF_BUFFERED_INPUT = inputSuppler; 
    } 
    currentInput = (T) INPUT_BUFFER; 
} 

/** 
* 
* @return the input to be used by test methods 
*/ 
public T getCurrentInput() { 
    return currentInput; 
} 

}