Я использую JUnit 4 для тестирования серверной системы с базой данных в памяти. Я использую @BeforeClass@До@После и @После занятий.
Пока что он отлично работает на уровне класса.
@BeforeClass содержит настройку базы данных, которая выполняется медленно, но ее нужно выполнять только один раз за сеанс тестирования.
@До просто стирает все, чтобы запустить следующий тест. Это довольно быстро.
Мои тесты выглядят примерно так:
class CompanyTest {
@BeforeClass
public static void beforeAll() throws Exception {
BeforeAll.run(); //Setup In Memory Database!!! Very time intensive!!!
}
@Before
public void beforeTest() throws Exception {
BeforeTest.run(); //Setup data in database. Relatively quick
}
@Test
public void testCreateCompany() throws Exception {
///...
}
@Test
public void testDeleteCompany() throws Exception {
///...
}
@Test
public void testAdminCompany() throws Exception {
///...
}
@After
public void afterTest() {
AfterTest.run(); //clear data
}
@AfterClass
public static void afterAll() throws Exception {
AfterAll.run(); //tear down database
}
}
Пока что он отлично работает на уровне класса.
Я могу щелкнуть правой кнопкой мыши (в Eclipse) по отдельному тесту, и он запустит @BeforeClass, а затем @До.
Я также могу щелкнуть (в Eclipse) сам класс, и он будет запускать @BeforeClass только один раз, а затем @До перед каждым тестом.
.... но как этот принцип распространяется на уровень Suite?
Я хочу запустить @BeforeClass перед всеми уроками в моем Suite. Если я напишу свой сюит так:
@Suite.SuiteClasses({ CompanyTest.class, CustomerTest.class, SomeOtherTest.class, })
public class AllTests {
@BeforeClass
public static void beforeAll() throws Exception {
BeforeAll.run();
}
@AfterClass
public static void afterAll() throws Exception {
AfterAll.run();
}
}
... Мне нужно удалить @BeforeClass из всех моих тестовых классов. Это раздражает, потому что у меня много тестовых классов, и я не хочу удалять свой @BeforeClass, потому что я хочу протестировать их предварительно.
В основном я говорю:
Есть ли простой способ (щелчком мыши в среде IDE) протестировать тесты JUnit на (а) уровне метода, (б) уровне класса и (в) уровне набора, сохраняя при этом процессы setUp и tearDown на уровне сеанса?
Предложение, которое я дал в аналогичный вопрос, заключалось бы в использовании правила JUnit (класса) и проверки внутри правила, если оно уже было инициализировано ранее, и пропустить инициализацию в этом случае. Это должно позволить вам повторно использовать правило как для наборов, так и для конкретных тестовых классов.




Итак, у меня есть аналогичная проблема (инициализация БД, пул соединений, кешированные данные и т. д.), Которую необходимо выполнить один раз перед запуском любых тестов. Обычно это делается при запуске сервера приложений. Для тестов, требующих этого, я добавил базовый класс:
public class BaseInitializedTest {
private boolean isInitialized;
@BeforeClass
public static void init() {
doOnetimeInit();
// Do other per unique class initialization here
}
private static synchronized void doOnetimeInit() {
if (!isInitialized) {
// Do you one time init here
isInitialized = true;
}
}
@AfterClass
public static void deinit() {
// Cleanup
}
}
А потом мои тесты, требующие инициализации:
public class SomethingTest extends BaseInitializedTest {
@Test
public testSomething() {
// Your test here
}
}
Не для всех тестов требуется инициализация стека, но те, которые это делают, наследуются от этого базового класса и будут правильно обрабатывать init. Надеюсь, это поможет.
Это не работает, потому что если у меня есть три разных класса, которые наследуются от BaseInitialisezedTest, и эти три класса настроены в моем Suite, то, если я запустил свой Suite, я все равно получу запуск @BeforeClass три раза (а не только один раз)
Конечно, в том-то и дело, что init будет вызываться все время, вам нужно убедиться, что фактическая инициализация выполняется один раз. Моя инициализация выполняется с помощью синглтона, поэтому повторные вызовы init ничего не сделают после первой инициализации кода. Используйте статический синхронизированный метод для инициализации, который устанавливает флаг при инициализации и, если установлен, просто возвращает. Я добавлю код для него выше.
Вам необходимо, чтобы глобальное состояние управлялось через разные точки входа. В вашем классе BeforeAll или AfterAll вы можете сохранить статическую ссылку, то есть AtomicBoolean, чтобы отслеживать, выполнили ли вы ее уже или нет.
Теперь проблема - с двумя отдельными классами для beforeAll и afterAll - какой класс должен отвечать за этот флаг.
Поэтому я предлагаю вам определить Rule, который содержит всю логику для установки и удаления (в основном весь код BeforeAll и AfterAll) и управляет глобальным состоянием для отслеживания инициализации.
В основном что-то вроде
class MyRule implements TestRule {
static final AtomicBoolean INITIALIZED = new AtomicBoolean();
@Override
public Statement apply(final Statement base, final Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
boolean needsTearDown = false;
try {
if (INITIALIZED.compareAndSet(false, true)) {
BeforeAll.run();
needsTearDown = true;
}
base.evaluate();
} finally {
if (needsTearDown) {
AfterAll.run();
INITIALIZED.set(false);
}
}
}
};
}
}
И в вашем Test and Suite просто добавьте
class YourTestOrSuite {
@ClassRule
public static MyRule rule = new MyRule();
}
Это не компилируется. Если я вставлю это в свою IDE, то скобки покажутся не в порядке.
(a) Уровень метода: BeforeAll не запускается, (b) Уровень класса: BeforeAll не запускается, (c) Уровень Suite: AfterAll запускается после первого класса, поэтому все другие классы терпят неудачу после того, как первый класс прошел.
Мое плохое, знак "!" в предложении if было неверно (compareAndSet возвращает true, если ожидаемое значение равно фактическому значению). Это происходит, когда вы кодируете прямо в SoW, не тестируя его :) ... теперь это исправлено и должно работать
Вместо удаления @BeforeClass из всех ваших тестовых классов вы можете добавить статический счетчик «nestingLevel», увеличивать его каждый BeforeAll.run (), уменьшать его каждый AfterAll.run () и выполнять фактическую настройку / удаление только тогда, когда nestingLevel равно 1.