1. Обзор
Как правило, при тестировании приложения, использующего JNDI, мы можем захотеть использовать фиктивный источник данных вместо реального. Это обычная практика при тестировании, чтобы сделать наши модульные тесты простыми и полностью отделенными от любого внешнего контекста.
В этом руководстве мы покажем, как протестировать фиктивный источник данных JNDI с помощью Spring Framework и библиотеки Simple-JNDI.
В этом руководстве мы сосредоточимся только на модульных тестах. Но обязательно ознакомьтесь с нашей статьей о том, как создать приложение Spring, используя JPA с источником данных JNDI .
2. Краткий обзор JNDI
Короче говоря, JNDI привязывает логические имена к внешним ресурсам, таким как соединения с базой данных . Основная идея заключается в том, что приложению не нужно ничего знать об определенном источнике данных, кроме его имени JNDI.
Проще говоря, все операции с именами относятся к контексту, поэтому, чтобы использовать JNDI для доступа к службе имен, нам нужно сначала создать объект InitialContext
. Как следует из названия, класс InitialContext
инкапсулирует начальный (корневой) контекст, который обеспечивает отправную точку для операций именования.
Проще говоря, корневой контекст действует как точка входа. Без него JNDI не сможет связать или найти наши ресурсы.
3. Как протестировать источник данных JNDI с помощью Spring
Spring обеспечивает готовую интеграцию с JNDI через SimpleNamingContextBuilder
. Этот вспомогательный класс предлагает отличный способ имитировать среду JNDI в целях тестирования.
Итак, давайте посмотрим, как мы можем использовать класс SimpleNamingContextBuilder
для модульного тестирования источника данных JNDI.
Во-первых, нам нужно создать исходный контекст именования для привязки и извлечения объекта источника данных :
@BeforeEach
public void init() throws Exception {
SimpleNamingContextBuilder.emptyActivatedContextBuilder();
this.initContext = new InitialContext();
}
Мы создали корневой контекст с помощью метода emptyActivatedContextBuilder()
, поскольку он обеспечивает большую гибкость по сравнению с конструктором, поскольку создает новый построитель или возвращает существующий.
Теперь, когда у нас есть контекст, давайте реализуем модульный тест, чтобы увидеть, как хранить и извлекать объект JDBC DataSource
с помощью JNDI:
@Test
public void whenMockJndiDataSource_thenReturnJndiDataSource() throws Exception {
this.initContext.bind("java:comp/env/jdbc/datasource",
new DriverManagerDataSource("jdbc:h2:mem:testdb"));
DataSource ds = (DataSource) this.initContext.lookup("java:comp/env/jdbc/datasource");
assertNotNull(ds.getConnection());
}
Как мы видим ,
мы используем метод bind()
для сопоставления нашего объекта JDBC DataSource
с именем java:comp/env/jdbc/datasource
.
Затем мы используем метод lookup()
для извлечения ссылки на DataSource
из нашего контекста JNDI, используя точное логическое имя, которое мы использовали ранее для привязки объекта JDBC DataSource .
Обратите внимание, что JNDI просто выдаст исключение, если указанный объект не будет найден в контексте.
Стоит отметить, что класс SimpleNamingContextBuilder
устарел, начиная с Spring 5.2, в пользу других решений, таких как Simple-JNDI .
4. Моделируйте и тестируйте источник данных JNDI с помощью Simple-JNDI
Simple-JNDI позволяет нам привязывать объекты, определенные в файлах свойств, к фиктивной среде JNDI . Он поставляется с отличной поддержкой для получения объектов типа javax.sql.DataSource
из JNDI вне контейнеров Java EE.
Итак, давайте посмотрим, как мы можем его использовать .
Во- первых, нам нужно добавить зависимость Simple-JNDI
к нашему pom.xml
:
<dependency>
<groupId>com.github.h-thurow</groupId>
<artifactId>simple-jndi</artifactId>
<version>0.23.0</version>
</dependency>
Последнюю версию библиотеки Simple-JNDI можно найти на Maven Central .
Далее мы собираемся настроить Simple-JNDI со всеми деталями, необходимыми для настройки контекста JNDI. Для этого нам нужно создать файл jndi.properties
, который необходимо поместить в путь к классам :
java.naming.factory.initial=org.osjava.sj.SimpleContextFactory
org.osjava.sj.jndi.shared=true
org.osjava.sj.delimiter=.
jndi.syntax.separator=/
org.osjava.sj.space=java:/comp/env
org.osjava.sj.root=src/main/resources/jndi
java.naming.factory.initial
определяет класс фабрики контекста, который будет использоваться для создания начального контекста.
org.osjava.sj.jndi.shared=true
означает, что все объекты InitialContext
будут совместно использовать одну и ту же память.
Как мы видим, мы использовали свойство org.osjava.sj.space
для определения java:/comp/env
в качестве отправной точки всех запросов JNDI.
Основная идея использования свойств org.osjava.sj.delimiter
и jndi.syntax.separator
заключается в том, чтобы избежать проблемы ENC .
Свойство org.osjava.sj.root
позволяет нам определить путь к месту хранения файлов свойств . В нашем случае все файлы будут находиться в папке src/main/resources/jndi
.
Итак, давайте определим объект javax.sql.DataSource
внутри нашего файла datasource.properties
:
ds.type=javax.sql.DataSource
ds.driver=org.h2.Driver
ds.url=jdbc:jdbc:h2:mem:testdb
ds.user=sa
ds.password=password
Теперь давайте создадим объект InitialContext
для нашего модульного теста:
@BeforeEach
public void setup() throws Exception {
this.initContext = new InitialContext();
}
Наконец, мы реализуем модульный тест для извлечения объекта DataSource
, уже определенного в файле datasource.properties
:
@Test
public void whenMockJndiDataSource_thenReturnJndiDataSource() throws Exception {
String dsString = "org.h2.Driver::::jdbc:jdbc:h2:mem:testdb::::sa";
Context envContext = (Context) this.initContext.lookup("java:/comp/env");
DataSource ds = (DataSource) envContext.lookup("datasource/ds");
assertEquals(dsString, ds.toString());
}
5. Вывод
В этом руководстве мы объяснили, как решить задачу тестирования JNDI вне контейнеров J2EE. Мы рассмотрели, как протестировать фиктивный источник данных JNDI с помощью Spring Framework и библиотеки Simple-JNDI.
Как всегда, код доступен на GitHub .