Статьи

Поддержка весеннего тестирования и кеширование контекста

Spring обеспечивает всестороннюю поддержку модульного и интеграционного тестирования — с помощью аннотаций для загрузки контекста приложения Spring, интеграции с инфраструктурами модульного тестирования, такими как JUnit и TestNG. Поскольку загрузка большого контекста приложения для каждого теста занимает время, Spring интеллектуально кэширует контекст приложения для набора тестов — обычно, когда мы выполняем тесты для проекта, например, через ant или maven, создается набор, охватывающий все тесты в проекте. ,

Есть несколько моментов, которые следует отметить при кэшировании, и это то, о чем я собираюсь здесь рассказать, это вряд ли будет всеобъемлющим, но основано на некоторых ситуациях, с которыми я столкнулся:

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='
  
 <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.