У нас есть несколько тестовых примеров JUnit (интеграционные тесты), и они логически сгруппированы в разные тестовые классы.
Мы можем загружать контекст приложения Spring один раз для каждого тестового класса и повторно использовать его для всех тестовых случаев в тестовом классе JUnit, как указано в http://static.springsource.org/spring/docs/current/spring-framework-reference /html/testing.html
Однако нам просто интересно, есть ли способ загрузить контекст приложения Spring только один раз для группы тестовых классов JUnit.
FWIW, мы используем Spring 3.0.5, JUnit 4.5 и Maven для сборки проекта.
spring
junit
junit4
spring-test
Рамеш
источник
источник
Ответы:
Да, это вполне возможно. Все, что вам нужно сделать, это использовать тот же
locations
атрибут в ваших тестовых классах:@ContextConfiguration(locations = "classpath:test-context.xml")
Spring кэширует контексты приложения по
locations
атрибуту, поэтому, если онlocations
появляется во второй раз, Spring использует тот же контекст, а не создает новый.Я написал статью об этой функции: Ускорение интеграционных тестов Spring . Также это подробно описано в документации Spring: 9.3.2.1 Управление контекстом и кеширование .
Это имеет интересный смысл. Поскольку Spring не знает, когда JUnit завершен, он навсегда кэширует весь контекст и закрывает их с помощью обработчика выключения JVM. Такое поведение (особенно когда у вас много тестовых классов с разными
locations
) может привести к чрезмерному использованию памяти, утечкам памяти и т. Д. Еще одно преимущество кеширования контекста.источник
Чтобы добавить к ответу Томаша Нуркевича , с Spring 3.2.2
@ContextHierarchy
аннотацию можно использовать для создания отдельной связанной множественной контекстной структуры. Это полезно, когда несколько тестовых классов хотят совместно использовать (например) настройки базы данных в памяти (источник данных, EntityManagerFactory, tx manager и т. Д.).Например:
@ContextHierarchy({ @ContextConfiguration("/test-db-setup-context.xml"), @ContextConfiguration("FirstTest-context.xml") }) @RunWith(SpringJUnit4ClassRunner.class) public class FirstTest { ... } @ContextHierarchy({ @ContextConfiguration("/test-db-setup-context.xml"), @ContextConfiguration("SecondTest-context.xml") }) @RunWith(SpringJUnit4ClassRunner.class) public class SecondTest { ... }
При такой настройке контекст, который использует "test-db-setup-context.xml", будет создан только один раз, но bean-компоненты внутри него могут быть введены в контекст отдельного модульного теста.
Подробнее о руководстве: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/testing.html#testcontext-ctx-management (поиск по запросу « иерархия контекста »)
источник
По сути, Spring достаточно умен, чтобы настроить это за вас, если у вас одинаковая конфигурация контекста приложения для разных тестовых классов. Например, допустим, у вас есть два класса A и B следующим образом:
@ActiveProfiles("h2") @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class A { @MockBean private C c; //Autowired fields, test cases etc... } @ActiveProfiles("h2") @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class B { @MockBean private D d; //Autowired fields, test cases etc... }
В этом примере класс A имитирует bean-компонент C, тогда как класс B имитирует bean-компонент D. Таким образом, Spring рассматривает их как две разные конфигурации и, таким образом, загружает контекст приложения один раз для класса A и один раз для класса B.
Если бы вместо этого мы хотели, чтобы Spring разделял контекст приложения между этими двумя классами, они должны были бы выглядеть следующим образом:
@ActiveProfiles("h2") @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class A { @MockBean private C c; @MockBean private D d; //Autowired fields, test cases etc... } @ActiveProfiles("h2") @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) public class B { @MockBean private C c; @MockBean private D d; //Autowired fields, test cases etc... }
Если вы подключите свои классы таким образом, spring загрузит контекст приложения только один раз либо для класса A, либо для класса B, в зависимости от того, какой из двух классов запускается первым в наборе тестов. Это может быть воспроизведено в нескольких тестовых классах, единственное условие состоит в том, что вы не должны настраивать тестовые классы по-другому. Любая настройка, которая приводит к тому, что тестовый класс будет отличаться от другого (в глазах весны), к весне приведет к созданию другого контекста приложения.
источник
создайте свой класс конфигурации, как показано ниже
@ActiveProfiles("local") @RunWith(SpringJUnit4ClassRunner.class ) @SpringBootTest(classes ={add your spring beans configuration classess}) @TestPropertySource(properties = {"spring.config.location=classpath:application"}) @ContextConfiguration(initializers = ConfigFileApplicationContextInitializer.class) public class RunConfigration { private ClassLoader classloader = Thread.currentThread().getContextClassLoader(); private static final Logger LOG = LoggerFactory.getLogger(S2BXISINServiceTest.class); //auto wire all the beans you wanted to use in your test classes @Autowired public XYZ xyz; @Autowired public ABC abc; } Create your test suite like below @RunWith(Suite.class) @Suite.SuiteClasses({Test1.class,test2.class}) public class TestSuite extends RunConfigration { private ClassLoader classloader = Thread.currentThread().getContextClassLoader(); private static final Logger LOG = LoggerFactory.getLogger(TestSuite.class); }
Создайте свои тестовые классы, как показано ниже
public class Test1 extends RunConfigration { @Test public void test1() { you can use autowired beans of RunConfigration classes here } } public class Test2a extends RunConfigration { @Test public void test2() { you can use autowired beans of RunConfigration classes here } }
источник
Один замечательный момент заключается в том, что если мы используем @SpringBootTests, но снова
use @MockBean in different test classes
, Spring не имеет возможности повторно использовать контекст своего приложения для всех тестов.Решение есть,
to move all @MockBean into an common abstract class
и это устраняет проблему.@SpringBootTests(webEnvironment = WebEnvironment.RANDOM_PORT, classes = Application.class) public abstract class AbstractIT { @MockBean private ProductService productService; @MockBean private InvoiceService invoiceService; }
Тогда тестовые классы можно увидеть, как показано ниже
public class ProductControllerIT extends AbstractIT { // please don't use @MockBean here @Test public void searchProduct_ShouldSuccess() { } } public class InvoiceControllerIT extends AbstractIT { // please don't use @MockBean here @Test public void searchInvoice_ShouldSuccess() { } }
источник