Интеграционное тестирование в Spring: TestContext Framework, конфигурация контекста с источниками тестовых свойств

Spring Framework имеет первоклассную поддержку понятия среды с иерархией источников свойств, и вы можете настроить интеграционные тесты с источниками свойств для конкретных тестов. В отличие от аннотации @PropertySource, используемой в классах @Configuration, вы можете объявить аннотацию @TestPropertySource в тестовом классе для объявления местоположений ресурсов для файлов тестовых свойств или встроенных свойств. Эти источники тестовых свойств добавляются к набору PropertySources в Environment для ApplicationContext, загруженного для аннотированного интеграционного теста.

Вы можете использовать @TestPropertySource с любой реализацией SPI SmartContextLoader, но @TestPropertySource не поддерживается с реализациями более старого SPI ContextLoader.

Реализации SmartContextLoader получают доступ к объединенным значениям источника свойств теста через методы getPropertySourceLocations() и getPropertySourceProperties() в MergedContextConfiguration.

Объявление источников тестовых свойств

Вы можете настроить файлы свойств теста, используя атрибут location или value в @TestPropertySource.

Поддерживаются как традиционные, так и основанные на XML форматы файлов свойств - например, "classpath:/com/example/test.properties" или "file:///path/to/file.xml".

Каждый путь интерпретируется как ресурс Spring. Простой путь (например, "test.properties") рассматривается как ресурс пути к классам, относящийся к пакету, в котором определен тестовый класс. Путь, начинающийся с косой черты, рассматривается как ресурс абсолютного пути к классам (например: "/org/example/test.xml"). Путь, который ссылается на URL-адрес (например, путь с префиксом classpath:, file: или http:), загружается с использованием указанного протокола ресурсов. Подстановочные знаки расположения ресурса (например, */.properties) не допускаются: каждое расположение должно оцениваться как точно один ресурс .properties или .xml.

В следующем примере используется файл тестовых свойств:

@ContextConfiguration
@TestPropertySource("/test.properties") 
class MyIntegrationTests {
    // тело класса...
}

Вы можете настроить встроенные свойства в виде пар ключ-значение с помощью атрибута свойств @TestPropertySource, как показано в следующем примере. Все пары "ключ-значение" добавляются во включающую среду как единый тестовый PropertySource с наивысшим приоритетом.

Поддерживаемый синтаксис для пар ключ-значение такой же, как синтаксис, определенный для записей в файле свойств Java:

  • key=value
  • key:value
  • key value

В следующем примере устанавливаются два встроенных свойства:

@ContextConfiguration
@TestPropertySource(properties = {"timezone = GMT", "port: 4242"}) 
class MyIntegrationTests {
    // тело класса...
}

Начиная с Spring Framework 5.2, @TestPropertySource можно использовать как повторяемую аннотацию. Это означает, что вы можете иметь несколько объявлений @TestPropertySource в одном тестовом классе, причем местоположения и свойства из более поздних аннотаций @TestPropertySource переопределяют свойства из предыдущих аналогичных аннотаций @TestPropertySource.

Кроме того, вы можете объявить несколько составных аннотаций в тестовом классе, каждая из которых снабжена метааннотациями с помощью @TestPropertySource, и все эти объявления @TestPropertySource будут вносить вклад в ваши источники тестовых свойств.

Аннотации @TestPropertySource с прямым присутствием всегда имеют приоритет над аннотациями @TestPropertySource с метаприсутствием. Другими словами, местоположения и свойства из непосредственно присутствующей аннотации @TestPropertySource будут переопределять местоположения и свойства из аннотации @TestPropertySource, используемой в качестве метааннотации.

Определение файла свойств по умолчанию

Если @TestPropertySource объявлен как пустая аннотация (то есть без явных значений для атрибутов местоположений или свойств), предпринимается попытка обнаружить файл свойств по умолчанию относительно класса, объявившего аннотацию. Например, если аннотированный тестовый класс - com.example.MyTest, соответствующий файл свойств по умолчанию - classpath:com/example/MyTest.properties. Если значение по умолчанию не может быть обнаружено, создается исключение IllegalStateException.

Приоритет

Свойства теста имеют более высокий приоритет, чем те, которые определены в среде операционной системы, свойствах системы Java или источниках свойств, добавленных приложением декларативно с помощью @PropertySource или программно. Таким образом, тестовые свойства могут использоваться для выборочного переопределения свойств, загруженных из источников свойств системы и приложения. Кроме того, встроенные свойства имеют более высокий приоритет, чем свойства, загруженные из местоположений ресурсов. Однако обратите внимание, что свойства, зарегистрированные через @DynamicPropertySource, имеют более высокий приоритет, чем свойства, загруженные через @TestPropertySource.

В следующем примере свойства часового пояса и порта, а также любые свойства, определенные в "/test.properties", переопределяют любые свойства с тем же именем, которые определены в источниках свойств системы и приложения. Более того, если файл "/test.properties" определяет записи для свойств часового пояса и порта, они переопределяются встроенными свойствами, объявленными с помощью атрибута properties. В следующем примере показано, как указать свойства как в файле, так и в строке:

@ContextConfiguration
@TestPropertySource(
    locations = "/test.properties",
    properties = {"timezone = GMT", "port: 4242"}
)
class MyIntegrationTests {
    // тело класса...
}

Наследование и переопределение источников тестовых свойств

@TestPropertySource поддерживает логические атрибуты inheritLocations и inheritProperties, которые указывают, должны ли наследоваться местоположения ресурсов для файлов свойств и встроенных свойств, объявленных суперклассами. Значение по умолчанию для обоих флагов - true. Это означает, что тестовый класс наследует местоположения и встроенные свойства, объявленные любыми суперклассами. В частности, местоположения и встроенные свойства для тестового класса добавляются к местоположениям и встроенным свойствам, объявленным суперклассами. Таким образом, подклассы имеют возможность расширять местоположения и встроенные свойства. Обратите внимание, что свойства, которые появляются позже, затеняют (т.е. переопределяют) свойства с тем же именем, которые появляются ранее. Кроме того, вышеупомянутые правила приоритета также применяются к унаследованным источникам тестовых свойств.

Если для атрибута inheritLocations или inheritProperties в @TestPropertySource установлено значение false, местоположения или встроенные свойства, соответственно, для тени тестового класса и эффективно заменяют конфигурацию, определенную суперклассами.

В следующем примере ApplicationContext для BaseTest загружается с использованием только файла base.properties в качестве источника свойства теста. Напротив, ApplicationContext для ExtendedTest загружается с использованием файлов base.properties и extended.properties в качестве местоположений источников тестовых свойств. В следующем примере показано, как определять свойства как в подклассе, так и в его суперклассе с помощью файлов свойств:

@TestPropertySource("base.properties")
@ContextConfiguration
class BaseTest {
    // ...
}

@TestPropertySource("extended.properties")
@ContextConfiguration
class ExtendedTest extends BaseTest {
    // ...
}

В следующем примере ApplicationContext для BaseTest загружается с использованием только встроенного свойства key1. Напротив, ApplicationContext для ExtendedTest загружается с использованием встроенных свойств key1 и key2. В следующем примере показано, как определять свойства как в подклассе, так и в его суперклассе, используя встроенные свойства:

@TestPropertySource(properties = "key1 = value1")
@ContextConfiguration
class BaseTest {
    // ...
}

@TestPropertySource(properties = "key2 = value2")
@ContextConfiguration
class ExtendedTest extends BaseTest {
    // ...
}


Читайте также:


Комментарии

Популярные сообщения из этого блога

Методы класса Object в Java

Как получить текущий timestamp в Java

Основные опции JVM для повышения производительности и отладки