Как проверить смоделированный источник данных JNDI с помощью Spring? - PullRequest
31 голосов
/ 09 мая 2011

Я довольно новичок в Spring, и мне интересно, как создавать тесты JUnit, использующие фиктивный источник данных, и как использовать с этим контекст JNDI?В настоящее время мое приложение использует JNDI-контекст из tomcat для извлечения соединения и через это соединение извлекает данные из базы данных.Поэтому я думаю, что мне нужно высмеивать вызовы JNDI и поиск данных.Любые хорошие указатели на то, что лучший способ справиться с этим было бы здорово!Большое спасибо!

Ответы [ 7 ]

34 голосов
/ 21 сентября 2011

Вы можете использовать SimpleNamingContextBuilder, чтобы сделать источник данных jndi доступным для ваших тестов:

    SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
    builder.bind("java:comp/env/jdbc/mydatasource", dataSource);
    builder.activate();

https://fisheye.springsource.org/browse/spring-framework/spring-test/src/main/java/org/springframework/mock/jndi/SimpleNamingContextBuilder.java?hb=true

Это не совсем насмешливый источник данных, но он делает источник данных доступным через jndi для ваших тестов.

32 голосов
/ 10 мая 2011

Я обычно определяю свои зависимости JNDI в отдельном файле, например datasource-context.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/jee
        http://www.springframework.org/schema/jee/spring-jee-3.0.xsd">

    <jee:jndi-lookup id="dataSource" 
        jndi-name="java:comp/env/dataSource" 
        expected-type="javax.sql.DataSource" />

</beans>

Чтобы в тестовых ресурсах я мог создать другой файл и определить источник тестовых данных, как мне удобно, например datasource-testcontext.xml:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource"
        p:driverClassName="org.hsqldb.jdbcDriver"
        p:url="jdbc:hsqldb:hsql://localhost:9001"
        p:username="sa"
        p:password="" /> 

</beans>

А затем в моем тестовом классе я использую тестовую конфигурацию источника данных вместо рабочей , которая зависит от JNDI:

@ContextConfiguration({
    "classpath*:META-INF/spring/datasource-testcontext.xml",
    "classpath*:META-INF/spring/session-factory-context.xml"
})
public class MyTest {

}
<Ч />

Если источник данных не определен в отдельном файле, вы все равно можете легко заглушить объект, возвращаемый вызовами JNDI:

4 голосов
/ 10 мая 2011

Вы можете создать свой собственный фиктивный источник данных, расширив SpringD AbstractDataSource.

import java.sql.Connection;
import java.sql.SQLException;

import org.springframework.jdbc.datasource.AbstractDataSource;

/**
 * Mock implementation of DataSource suitable for use in testing.
 * 
 *
 */
public class MockDataSource extends AbstractDataSource {
    private Connection connection;

    /**
     * Sets the connection returned by javax.sql.DataSource#getConnection()
     * and javax.sql.DataSource#getConnection(java.lang.String, java.lang.String)
     * 
     * @param connection
     */
    public void setConnection(Connection connection) {
        this.connection = connection;
    }

    /*
     * (non-Javadoc)
     * @see javax.sql.DataSource#getConnection()
     */
    public Connection getConnection()
            throws SQLException {
        return connection;
    }

    /*
     * (non-Javadoc)
     * @see javax.sql.DataSource#getConnection(java.lang.String, java.lang.String)
     */
    public Connection getConnection(String username, String password)
            throws SQLException {
        return connection;
    }
}

Я бы отделил поиск соединения JNDI от остальной части кода.Добавьте источник данных в свои объекты доступа к данным (DAO) и используйте MockDataSource для тестирования DAO.

2 голосов
/ 06 августа 2013

Вы всегда можете создать конфигурацию beans.test.xml, где вы сначала ссылаетесь на beans.xml, а затем переопределяете конфигурацию источника данных:

src / main / resources / beans.xml

<!-- Database configuration -->
<import resource="beans.datasource.jndi.xml" />

src / test / resources / beans.test.xml

<import resource="beans.xml" />
<import resource="beans.datasource.test.xml" />

JUnit Test Class:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/beans.test.xml" })
public class ASRTests
{
...
}

В вашем bean-компоненте jndi объявите ссылку

<jee:jndi-lookup expected-type="javax.sql.DataSource" id="mysqlDataSource" jndi-name="jdbc/mysql"/>

В тестовом бине объявите источник данных

<bean id="mysqlDataSource" ...>
...
</bean>

Помните, что для перемещения тестового бина источника данных в тестовую папку.

1 голос
/ 27 апреля 2015

Spring's org.springframework.jndi.JndiObjectFactoryBean лучше всего подходит для поиска JNDI. Согласно документации, он также позволяет вводить значения по умолчанию для тестовых случаев на основе пружин.

См. Нижеприведенную конфигурацию пружины (названную spring-test-db-config.xml)

<bean id="dataSource" class="oracle.jdbc.pool.OracleDataSource">
    <property name="URL" value="jdbc:oracle:thin:@localhost:1521:XE"/>
    <property name="user" value="UNITTEST"/>
    <property name="password" value="UNITTEST"/>
</bean>

<bean id="dataSourceFromJndi" class="org.springframework.jndi.JndiObjectFactoryBean">
    <!-- Any junk value will suffice as that is always gonna throw NamingException -->
    <property name="jndiName" value="jdbc/Ds"/>
    <property name="defaultObject" ref="dataSource"/>
</bean>

Добавление компонента, определенного в другом файле конфигурации, должно ссылаться на dataSourceFromJndi компонент

<!-- START OF SERVICES -->
<bean class="com.sample.Service" >
    <property name="dataSource" ref="dataSourceFromJndi" />
</bean>

Преимущество этого подхода заключается в том, что вы можете хранить 2 разных файла конфигурации БД - один для производства и другой для модульного тестирования. Просто импортируйте правильный. Тестовая конфигурация будет содержать объект по умолчанию.

0 голосов
/ 09 сентября 2017

Вы также можете использовать Simple-JNDI.Это реализация JNDI в памяти для работы с контекстами JNDI вне контейнера J2EE.Это позволяет использовать один и тот же файл определения компонента в производстве и тестировании.Предположим, что это ваше определение bean-компонента:

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/jdbc/DataSource"/>
</bean>
<bean id="dao" class="my.Dao">
    <property name="dataSource" ref="dataSource" />
</bean>

Создайте файл свойств, подобный этому

type=javax.sql.DataSource
driverClassName=org.gjt.mm.mysql.Driver
url=jdbc:mysql://localhost/testdb
username=user_name
password=password

Поместите Simple-JNDI и файл jndi.properties с небольшой конфигурацией в ваш путь к классам,Затем получите доступ к источнику данных как обычно.

Подробнее о Simple-JNDI можно найти здесь.

0 голосов
/ 30 декабря 2015

Java Config .....

Тестовый набор Junit

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {DatabaseConfigStub.class}, loader= AnnotationConfigContextLoader.class)
public class DatabaseConfigTest  {

@Autowired
private DataSource datasource;
@Autowired
private JdbcTemplate jdbcTemplate;


@Before
public void setUp() throws Exception {

}

@After
public void tearDown() throws Exception {
}

@Test
public void testDataSource() {
    assertNotNull(datasource);
    assertNotNull(jdbcTemplate);
}

}

DatabaseConfigStub

public class DatabaseConfigStub {

private static final Logger log = Logger.getLogger(DatabaseConfigStub.class);

        private static final String DS_NAME = "jdbc/DS_NAME";

@Bean
DataSource dataSource() {
    JndiObjectFactoryBean jndiObjectBean = EasyMock.createMock(JndiObjectFactoryBean.class);
    jndiObjectBean.setJndiName(DS_NAME);
    jndiObjectBean.setResourceRef(true);
    jndiObjectBean.setProxyInterfaces(DataSource.class);

    EasyMock.expect( (DataSource)jndiObjectBean.getObject()).andReturn(new DataSource() {

            public <T> T unwrap(Class<T> iface) throws SQLException {
                // TODO Auto-generated method stub
                return null;
            }

            public boolean isWrapperFor(Class<?> iface) throws SQLException {
                // TODO Auto-generated method stub
                return false;
            }

            public void setLoginTimeout(int seconds) throws SQLException {
                // TODO Auto-generated method stub

            }

            public void setLogWriter(PrintWriter out) throws SQLException {
                // TODO Auto-generated method stub

            }

            public int getLoginTimeout() throws SQLException {
                // TODO Auto-generated method stub
                return 0;
            }

            public PrintWriter getLogWriter() throws SQLException {
                // TODO Auto-generated method stub
                return null;
            }

            public Connection getConnection(String username, String password)
                    throws SQLException {
                // TODO Auto-generated method stub
                return null;
            }

            public Connection getConnection() throws SQLException {
                // TODO Auto-generated method stub
                return null;
            }
        }
    );
    EasyMock.replay(jndiObjectBean);

    return (DataSource) jndiObjectBean.getObject();
}

@Bean
JdbcTemplate jdbcTemplate(){
    return new JdbcTemplate( dataSource());
}

}

...