Обеспечение того, чтобы тест подкласса JUnit переопределял метод @BeforeClass
У меня есть то, что составляет легкий тестовый фреймворк, написанный как JUnit Abstract test. То, что я хотел бы сделать, это иметь реализующие подклассы, каждый из которых определяет свои собственные настройки класса тестирования. Мой план состоял в том, чтобы абстрактный суперкласс определял @BeforeClass
метод, который вызывает абстрактный метод установки, который каждый подкласс должен был бы определить, но это терпит неудачу, так как @BeforeClass
методы должны быть статическими, а статические методы не могут быть абстрактными и не могут вызывать методы экземпляра.
Я мог бы просто предположить, что подклассы будут выполнять настройку, включая то, что требуется в документации, или бросая IllegalStateException
, но я бы очень хотел иметь возможность применять это на уровне интерфейса по ряду причин. Кто-нибудь может придумать способ обойти это?
Кстати, у меня была та же проблема с параметризацией этих тестов (подклассы определяют параметры, но @Parameters
аннотированные методы должны быть статическими). Я справился с этим, запустив сторонний бегун JUnitParams, который допускает параметры уровня метода. Проверьте это здесь: https://github.com/Pragmatists/JUnitParams
4 ответа
Один вариант состоит в том, чтобы подклассы реализовали, скажем, статический doSetupOnce()
метод, и найти и вызвать этот метод рефлексивно из базового класса @BeforeClass
метод. Поскольку это должен быть статический метод, его существование может быть применено только во время выполнения.
Другой подход будет иметь абстрактную doSetupOnce
Метод экземпляра в базовом классе, который вызывается в первый раз @Before
метод вызывается. Это усиливает проблему во время компиляции, но тогда разработчики должны быть осторожны, чтобы не получить доступ к полям экземпляра из этого метода (так как это, вероятно, не то, что они хотят).
В общем (и не зная подробностей вашей ситуации), я не очень люблю любой из этих подходов и предпочел бы, чтобы разработчики объявили @BeforeClass
метод, если это необходимо. Запирание их в жесткой схеме базового класса может вызвать больше проблем, чем решить. Также рассмотрите использование правил JUnit, которые часто являются лучшим выбором, чем базовые классы (например, потому что они являются компонуемыми). Конечно, вы также можете объединить два подхода, полагаясь в основном на правила JUnit и дополнительно предлагая некоторые базовые классы с предопределенными правилами для удобства.
Это может быть вне сферы применения или излишним, но я думаю, что стоит упомянуть, поскольку они не так уж отличаются. Поэтому я делаю прыжок в веру и предлагаю вам попробовать TestNG, потому что методы, аннотированные @BeforeClass, не обязательно должны быть статичными, равно как и методы, аннотированные @Parameters.
Вы можете прочитать о различиях между двумя платформами здесь, и похоже, что они также поддерживают перенос тестов JUnit в TestNG
На ваш главный вопрос, почему бы не сделать ваш родительский класс абстрактным и использовать аннотацию @Before вместо @BeforeClass? Например:
public abstract class TestParent {
@Before
public void setup() {
doSetup();
}
protected abstract void doSetup();
// other stuff...
}
public class RealTest extends TestParent {
protected void doSetup() {
// custom setup
}
// custom tests...
}
Это заставит подклассы переопределить метод doSetup() без использования статических методов.
Я не думаю, что это возможно сделать чистым способом. Не только @BeforeClass
статический метод, но JUnit будет вызывать родительский @BeforeClass
до ребенка @BeforeClass
,
В любом случае, если вы попытаетесь это сделать, вы должны обязательно раскрыть внутреннее статическое состояние родительского класса, чтобы дочерний класс мог устанавливать родительские поля, нарушая инкапсуляцию.
Я думаю, что лучший способ состоит в том, чтобы использовать @Before, но также иметь статический флаг, который устанавливает, если метод был вызван ранее, таким образом, по крайней мере, вы можете закорачивать и выполнять инициализацию только для первого вызова...