Как объединить @Rule и @ClassRule в JUnit 4.12
Согласно примечаниям к выпуску 4.12, статические члены тестового класса можно аннотировать с помощью @Rule и @ClassRule:
статический член с аннотациями @Rule и @ClassRule теперь считается действительным. Это означает, что одно правило может использоваться для выполнения действий как до / после класса (например, настройка / удаление внешнего ресурса), так и между тестами (например, сброс внешнего ресурса),
Я хочу использовать эту функцию, чтобы инициализировать ресурс в начале всех тестов в файле, выполнять некоторую очистку ресурса между каждым тестом и избавляться от него после завершения всех тестов. Этот ресурс в настоящее время представлен классом, который расширяет ExternalResource.
В моем before
а также after
методы, как я могу провести различие между "до / после всех тестов" и "до / после каждого теста"? Нужно ли использовать другую / нестандартную реализацию TestRule
чтобы сделать это?
4 ответа
Вы можете реализовать TestRule#apply
и использовать isTest
а также isSuite
методы Description
чтобы определить, какой Statement
ваш TestRule
применяется к.
Вот пример интерфейса, который вы могли бы построить, чтобы дать правило, которое имеет полный before
, after
, verify
, beforeClass
, afterClass
, verifyClass
Тип поведения:
public interface CombinedRule extends TestRule {
default Statement apply(Statement base, Description description) {
if (description.isTest()) {
return new Statement() {
public void evaluate() throws Throwable {
before();
try {
base.evaluate();
verify();
} finally {
after();
}
}
};
}
if (description.isSuite()) {
return new Statement() {
public void evaluate() throws Throwable {
beforeClass();
try {
base.evaluate();
verifyClass();
} finally {
afterClass();
}
}
};
}
return base;
}
default void before() throws Exception {
//let the implementer decide whether this method is useful to implement
}
default void after() {
//let the implementer decide whether this method is useful to implement
}
/**
* Only runs for Tests that pass
*/
default void verify() {
//let the implementer decide whether this method is useful to implement
}
default void beforeClass() throws Exception {
before();
}
default void afterClass() {
after();
}
/**
* Only runs for Suites that pass
*/
default void verifyClass() {
verify();
}
}
Методы, отмеченные @Before
а также @After
будет выполняться до и после каждого теста, в то время как те, @BeforeClass
а также @AfterClass
будет выполняться до и после первого / последнего теста в классе соответственно.
Методы до / после @Rule
выполняются до и после каждого теста, а методы до / после @ClassRule
запускаются до / после всего тестового класса.
Вы можете использовать ExternalResource либо для @Rule
или же @ClassRule
до тех пор, пока методы-обработчики реагируют правильно для обоих сценариев. Насколько я могу судить из документации, нет никаких средств для разграничения двух категорий правил в методах класса правил. Если вы используете класс правил для обоих случаев, он будет применяться одинаково для обоих.
Вы не можете различить @BeforeClass
а также @Before
или же @AfterClass
а также @After
, Более подробную информацию о причине добавления этой функции можно найти в запросе.
Вы можете создать свое собственное правило, которое реализует оба TestRule
а также MethodRule
:
public SharableExternalResource implements TestRule, MethodRule {
public final Statement apply(
final Statement base, Description description) {
return
new Statement() {
@Override
public void evaluate() throws Throwable {
beforeClass();
List<Throwable> errors = new ArrayList<Throwable>();
try {
base.evaluate();
} catch (Throwable t) {
errors.add(t);
} finally {
try {
afterClass();
} catch (Throwable t) {
errors.add(t);
}
}
MultipleFailureException.assertEmpty(errors);
}
};
}
public final Statement apply(
Statement base, FrameworkMethod method, Object target) {
return
new Statement() {
@Override
public void evaluate() throws Throwable {
before();
List<Throwable> errors = new ArrayList<Throwable>();
try {
base.evaluate();
} catch (Throwable t) {
errors.add(t);
} finally {
try {
after();
} catch (Throwable t) {
errors.add(t);
}
}
MultipleFailureException.assertEmpty(errors);
}
};
}
public void beforeClass() throws Exception {
// do nothing
}
protected void before() throws Exception {
// do nothing
}
protected void after() throws Exception {
// do nothing
}
public void afterClass() throws Exception {
// do nothing
}
}