Перефразируя вопрос: вам нужен пакет JUnit с хуками «до всех» и «после», которые также будут запускаться при запуске тестов один за другим (например, из IDE).
AFAIK JUnit 4 не предоставляет ничего готового для этого, но если вы согласны с включением некоторых сторонних приложений Spring (spring-test и spring-context) в ваш проект, я могу предложить обходной путь, который я использовал.
Полный пример того, что описано в этом вопросе может быть найдено здесь.
Решение (с использованием Spring)
Мы будем использовать контекст Spring для реализации нашей инициализации и очистки. Давайте добавим базовый класс для наших тестов:
@ContextConfiguration(initializers = AbstractTestClass.ContextInitializer.class)
public class AbstractTestClass {
@ClassRule
public final static SpringClassRule springClassRule = new SpringClassRule();
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
public static class ContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
System.out.println("Initializing context");
context.addApplicationListener(
(ApplicationListener<ContextClosedEvent>)
contextClosedEvent ->
System.out.println("Closing context"));
}
}
}
Обратите внимание на SpringClassRule
и SpringMethodRule
Правила JUnit, которые улучшают наш базовый класс с помощью Spring-superpowers (Обработка аннотаций Spring test - ContextConfiguration
в этом случае, но в там - см. Справочник по тестированию Spring а> подробнее). Вы можете использовать SpringRunner
для этой цели, но это гораздо менее гибкое решение (поэтому опущено).
Тестовые классы:
public class TestClass1 extends AbstractTestClass {
@Test
public void test() {
System.out.println("TestClass1 test");
}
}
public class TestClass2 extends AbstractTestClass {
@Test
public void test() {
System.out.println("TestClass2 test");
}
}
И набор тестов:
@RunWith(Suite.class)
@SuiteClasses({TestClass1.class, TestClass2.class})
public class TestSuite {
}
Вывод при запуске пакета (для краткости удалены журналы Spring):
Initializing context
TestClass1 test
TestClass2 test
Closing context
Вывод при запуске одного теста (TestClass1
):
Initializing context
TestClass1 test
Closing context
Слово объяснения
Это работает благодаря кэшированию контекста Spring. Цитата из документов:
Как только платформа TestContext загружает ApplicationContext
(или WebApplicationContext
) для теста, этот контекст кэшируется и повторно используется для всех последующих тестов, которые объявляют ту же уникальную конфигурацию контекста в том же наборе тестов. Чтобы понять, как работает кэширование, важно понимать, что подразумевается под «уникальным» и «набором тестов».
-- https://docs.spring.io/spring/docs/5.1.2.RELEASE/spring-framework-reference/testing.html#testcontext-ctx-management-caching
Помните, что вы получите другой контекст (и другую инициализацию), если переопределите конфигурацию контекста (например, добавите еще один инициализатор контекста с ContextConfiguration
) для любого из классов в иерархии (TestClass1
или TestClass2
в нашем примере).
Использование bean-компонентов для совместного использования экземпляров
Вы можете определить bean-компоненты в своем контексте. Они будут общими для всех тестов, использующих один и тот же контекст. Это может быть полезно для совместного использования объекта в наборе тестов (контейнер Testcontainers в вашем случае, судя по тегам).
Добавим боб:
@ContextConfiguration(initializers = AbstractTestClass.ContextInitializer.class)
public class AbstractTestClass {
@ClassRule
public final static SpringClassRule springClassRule = new SpringClassRule();
@Rule
public final SpringMethodRule springMethodRule = new SpringMethodRule();
public static class ContextInitializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext context) {
ADockerContainer aDockerContainer = new ADockerContainer();
aDockerContainer.start();
context.getBeanFactory().registerResolvableDependency(
ADockerContainer.class, aDockerContainer);
context.addApplicationListener(
(ApplicationListener<ContextClosedEvent>)
contextClosedEvent ->
aDockerContainer.stop());
}
}
}
И внедрить его в тестовые классы:
public class TestClass1 extends AbstractTestClass {
@Autowired
private ADockerContainer aDockerContainer;
@Test
public void test() {
System.out.println("TestClass1 test " + aDockerContainer.getData());
}
}
public class TestClass2 extends AbstractTestClass {
@Autowired
private ADockerContainer aDockerContainer;
@Test
public void test() {
System.out.println("TestClass2 test " + aDockerContainer.getData());
}
}
ADockerContainer
класс:
public class ADockerContainer {
private UUID data;
public void start() {
System.out.println("Start container");
data = UUID.randomUUID();
}
public void stop() {
System.out.println("Stop container");
}
public String getData() {
return data.toString();
}
}
(Пример) вывод:
Start container
TestClass1 test 56ead80b-ec34-4dd6-9c0d-d6f07a4eb0d8
TestClass2 test 56ead80b-ec34-4dd6-9c0d-d6f07a4eb0d8
Stop container
person
jannis
schedule
14.11.2018
env
в ваши тестовые классы. Кроме того, любые поля, отмеченные@Rule
или@ClassRule
, должны быть общедоступными a>, которые также работают только с подтипамиTestRule
, где я не уверен, является лиDockerContainer
одним из них. Посколькуenv
уже является статическим, вы можете получить к нему доступ в своих тестовых классах черезJUnitTest.env
после изменения его на общедоступный, что вам все равно придется сделать. - person Roman Vottner   schedule 28.09.2018