Перефразируя вопрос: вам нужен пакет 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(Обработка аннотации пружинного теста - в данном случае ContextConfiguration
, но здесь есть еще много полезного - подробности см. Ссылка на весеннее тестирование ).Вы можете использовать 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
в нашем примере).
Использование бобов для совместного использования экземпляров
Вы можете определить бины в своем контексте.Они будут доступны для всех тестов в одном контексте.Это может быть полезно для совместного использования объекта в наборе тестов (контейнер Testcontainers в вашем случае, судя по тегам).
Давайте добавим bean-компонент:
@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