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!
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:
org.springframework.mock.jndi
, dh. SimpleNamingContextBuilder
(es gibt ein Beispiel im Javadoc dieses Kalasses).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();
Dies ist nicht genau mocking der Datenquelle, aber sie macht die Datenquelle über jndi für Ihre Tests verfügbar.
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.
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.
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.
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());
}
}
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.