Есть несколько моментов, которые следует отметить при кэшировании, и это то, о чем я собираюсь здесь рассказать, это вряд ли будет всеобъемлющим, но основано на некоторых ситуациях, с которыми я столкнулся:
1. Кэширование основано на расположении файлов контекста приложения Spring.
Рассмотрим пример файла конфигурации Spring:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
<? xml version = '1.0' encoding = 'UTF-8' standalone = 'no' ?> xsi:schemaLocation=' http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd'> < bean id = 'user1' class = 'org.bk.lmt.domain.TaskUser' p:username = 'user1' p:fullname = 'testUser1' /> < bean name = 'user2' class = 'org.bk.lmt.domain.TaskUser' p:username = 'user2' p:fullname = 'testUser' /> < bean class = 'org.bk.contextcaching.DelayBean' /> </ beans > |
И пример теста для загрузки этого файла контекста и проверки чего-либо.
01
02
03
04
05
06
07
08
09
10
11
|
@RunWith (SpringJUnit4ClassRunner. class ) @ContextConfiguration (locations = { 'contexttest.xml' }) public class Test1 { @Autowired Map<String, TaskUser> usersMap; @Test public void testGetAUser() { TaskUser user = usersMap.get( 'user1' ); assertThat(user.getFullname(), is( 'testUser1' )); } } |
Я намеренно добавил компонент (DelayBean), для создания которого требуется около 2 секунд, чтобы имитировать контексты приложения Spring, которые медленно загружаются.
Если я сейчас запускаю небольшой набор тестов с двумя тестами, каждый из которых использует один и тот же контекст приложения, то поведение таково, что первый тест проходит около 2 секунд, но второй тест выполняется быстро из-за кэширования контекста.
Если бы был третий тест с использованием другого контекста приложения, этот тест снова потребовал бы времени для запуска, поскольку новый контекст приложения должен быть загружен:
1
2
3
4
5
|
@RunWith (SpringJUnit4ClassRunner. class ) @ContextConfiguration (locations = { 'contexttest2.xml' }) public class Test3 { ... } |
2. Кэширование контекстов приложения учитывает активный профиль, в котором выполняется тест — по сути, профиль также является частью внутреннего ключа, который Spring использует для кэширования контекста, поэтому, если два теста используют один и тот же контекст приложения, но разные профили активным для каждого из тестов, тогда кэшированный контекст приложения не будет использоваться для второго теста:
01
02
03
04
05
06
07
08
09
10
|
@RunWith (SpringJUnit4ClassRunner. class ) @ContextConfiguration (locations = { 'contexttest.xml' }) @ActiveProfiles ( 'dev1' ) public class Test1 { .... @RunWith (SpringJUnit4ClassRunner. class ) @ContextConfiguration (locations = { 'contexttest.xml' }) @ActiveProfiles ( 'dev2' ) public class Test2 { .... |
3. Кэширование контекста приложения применяется даже при новом стиле @Configuration, который определяет контекст приложения и использует его в тестах:
1
2
3
4
5
6
7
8
|
@RunWith (SpringJUnit4ClassRunner. class ) @ContextConfiguration (classes={TestConfiguration. class }) public class Test1 { ... @RunWith (SpringJUnit4ClassRunner. class ) @ContextConfiguration (classes={TestConfiguration. class }) public class Test2 { .... |
Одним из следствий кэширования является то, что если тестовый класс изменяет состояние bean-компонента, тогда другой класс в наборе тестов, который использует контекст кэшированного приложения, в конечном итоге увидит модифицированный bean-компонент вместо bean-компонента, как он был определен в контексте приложения :
Например, рассмотрим два теста, каждый из которых изменяет бин в контексте, но утверждает состояние, как оно определено в контексте приложения. Здесь один из тестов может закончиться неудачей (в зависимости от порядка, в котором Junit выполняет тесты). ):
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
@RunWith (SpringJUnit4ClassRunner. class ) @ContextConfiguration (classes={TestConfiguration. class }) public class Test1 { @Autowired Map<String, TaskUser> usersMap; @Test public void testGetAUser1() { TaskUser user = usersMap.get( 'user1' ); assertThat(user.getFullname(), is( 'testUser1' )); user.setFullname( 'New Name' ); } @Test public void testGetAUser2() { TaskUser user = usersMap.get( 'user1' ); assertThat(user.getFullname(), is( 'testUser1' )); user.setFullname( 'New Name' ); } } |
Исправление состоит в том, чтобы указать поддержке тестов Spring, что контекст приложения теперь грязный и должен быть перезагружен для других тестов, и это делается с помощью аннотации @DirtiesContext, которая может указываться на уровне класса тестирования или уровня метода тестирования.
1
2
3
4
|
@Test @DirtiesContext public void testGetAUser2() { ... |
Приятного кодирования и не забудьте поделиться!
Ссылка: поддержка весеннего тестирования и кеширование контекста от нашего партнера JCG Биджу Кунджуммена в блоге all and sundry.