webentwicklung-frage-antwort-db.com.de

Wie teste ich eine JNDI-Datenquelle mit Spring?

Ich bin ziemlich neu in Spring und frage mich, wie man JUnit-Tests erstellt, die eine nachgeahmte Datenquelle verwenden, und wie man damit einen JNDI-Kontext verwendet. Derzeit verwendet meine Anwendung einen JNDI-Kontext von Tomcat, um eine Verbindung abzurufen, und über diese Verbindung werden Daten aus einer Datenbank abgerufen. Ich denke, ich muss die JNDI-Aufrufe und den Datenabruf verspotten. Jeder gute Hinweis darauf, was der beste Weg ist, dies zu bewältigen, wäre großartig! Danke vielmals!

30
Marco

Normalerweise definiere ich meine JNDI-Abhängigkeiten in separaten Dateien, wie 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>

Damit ich in Testressourcen eine andere Datei erstellen und die Testdatenquelle definieren kann, wie es für mich zutrifft, wie 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>

Und dann benutze ich in meiner Testklasse die Testkonfiguration der Datenquelle anstelle der Produktion, die von JNDI abhängt:

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

}

Wenn die Datenquelle nicht in einer separaten Datei definiert ist Sie können das von JNDI-Aufrufen zurückgegebene Objekt weiterhin problemlos stubben:

32
Roadrunner

Sie können SimpleNamingContextBuilder verwenden, um Ihren Tests eine Jndi-Datenquelle zur Verfügung zu stellen:

    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

Dies ist nicht genau mocking der Datenquelle, aber sie macht die Datenquelle über jndi für Ihre Tests verfügbar.

33
prule

Sie können Ihre eigene Mock-DataSource erstellen, indem Sie Spring's AbstractDataSource erweitern.

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;
    }
}

Ich würde die JNDI-Suche der Verbindung vom Rest des Codes trennen. Injizieren Sie die DataSource in Ihre Data Access Objects (DAOs) und verwenden Sie die MockDataSource zum Testen der DAOs.

4
Paul Croarkin

Sie können immer eine Beans.test.xml-Konfiguration erstellen, in der Sie zuerst auf die Beans.xml verweisen und die Datenquellenkonfiguration überschreiben:

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-Testklasse:

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

Erklären Sie in Ihrer Jndi-Bean die Referenz 

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

Deklarieren Sie in Ihrer Test-Bean die Datenquelle

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

Denken Sie daran, das Test-Datasource-Bean in den Testordner zu verschieben.

2
Hannes

Spring's org.springframework.jndi.JndiObjectFactoryBean eignet sich am besten für JNDI-Lookups. Wie in der Dokumentation beschrieben, können auch Standardwerte für federbasierte Testfälle eingegeben werden. 

Siehe die folgende Federkonfiguration (genannt 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>

Das in einer anderen Konfigurationsdatei definierte Add-Bean muss sich auf dataSourceFromJndi-Bean beziehen

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

Der Vorteil dieses Ansatzes besteht darin, dass Sie zwei unterschiedliche DB-Konfigurationsdateien aufbewahren können - eine für die Produktion und die andere für Komponententests. Importiere einfach das richtige. Die Testkonfiguration enthält ein Standardobjekt.

1
Mohit

Java Config ..... 

Junit-Testfall

@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());
}

}

0
Vilish

Sie können auch Simple-JNDI verwenden. Es ist eine JNDI-Implementierung im Arbeitsspeicher für die Arbeit mit JNDI-Kontexten außerhalb eines J2EE-Containers. Damit können Sie dieselbe Bean-Definitionsdatei in Produktion und Test verwenden. Angenommen, dies ist Ihre Bean-Definition in der Produktion:

<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>

Erstellen Sie eine Eigenschaftsdatei wie diese

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

Fügen Sie Simple-JNDI und eine jndi.properties-Datei mit einer kleinen Konfiguration in Ihren Klassenpfad ein. Greifen Sie dann wie üblich auf Ihre Datenquelle zu.

Mehr über Simple-JNDI finden Sie hier.

0
Holger Thurow