如何用Springtesting一个模拟的JNDI数据源?
我对Spring相当陌生,想知道如何创build使用模拟数据源的JUnittesting,以及如何使用JNDI上下文? 目前我的应用程序使用来自tomcat的JNDI上下文来检索连接,并通过该连接从数据库中检索数据。 所以我想我需要模拟JNDI调用和数据检索。 任何好的方法来解决这个问题的方法是非常好的! 非常感谢!
我通常在独立文件中定义我的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>
因此,在testing资源中,我可以创build另一个文件并定义testing数据源,但是它适合我,比如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>
然后在我的testing类中使用数据源的testingconfiguration,而不是生产依赖于JNDI的configuration:
@ContextConfiguration({ "classpath*:META-INF/spring/datasource-testcontext.xml", "classpath*:META-INF/spring/session-factory-context.xml" }) public class MyTest { }
如果数据源未在单独的文件中定义您仍然可以轻松地对由JNDI调用返回的对象进行存根:
- 像这样: 在容器之外注入JUnittesting的JNDI数据源
- 或者在包
org.springframework.mock.jndi
使用类。SimpleNamingContextBuilder
(在这个calass的javadoc中有一个例子)。
您可以使用SimpleNamingContextBuilder将jndi数据源提供给您的testing:
SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder(); builder.bind("java:comp/env/jdbc/mydatasource", dataSource); builder.activate();
这并不是完全嘲笑数据源,但它确实可以通过jndi为您的testing提供数据源。
你可以通过扩展Spring的AbstractDataSource来创build自己的模拟DataSource。
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查找。 将DataSource注入到数据访问对象(DAO)中,并使用MockDataSource来testingDAO。
您可以总是创build一个beans.test.xmlconfiguration,您首先引用beans.xml,然后覆盖数据源configuration:
SRC /主/资源/的beans.xml
<!-- Database configuration --> <import resource="beans.datasource.jndi.xml" />
SRC /testing/资源/ beans.test.xml
<import resource="beans.xml" /> <import resource="beans.datasource.test.xml" />
JUnittesting类:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath:/beans.test.xml" }) public class ASRTests { ... }
在你的jndi bean中,声明引用
<jee:jndi-lookup expected-type="javax.sql.DataSource" id="mysqlDataSource" jndi-name="jdbc/mysql"/>
在你的testingbean中,声明数据源
<bean id="mysqlDataSource" ...> ... </bean>
请记住将testing数据源bean移动到testing文件夹中。
Spring的org.springframework.jndi.JndiObjectFactoryBean
最适合于JNDI查找。 按照它的文档,它允许为基于spring的testing用例注入默认值。
参考下面的springconfiguration(命名为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>
定义在其他configuration文件上的bean应该引用dataSourceFromJndi
bean
<!-- START OF SERVICES --> <bean class="com.sample.Service" > <property name="dataSource" ref="dataSourceFromJndi" /> </bean>
这种方法的优点是可以保存2个不同的DBconfiguration文件 – 一个用于生产,另一个用于unit testing。 只要input正确的一个。 testingconfiguration将包含一个默认对象。
Javaconfiguration…..
Junittesting用例
@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()); }
}
你也可以使用Simple-JNDI。 这是一个内存JNDI实现,用于处理J2EE容器之外的JNDI上下文。 它允许您在生产和testing中使用相同的bean定义文件。 假设这是您在生产中的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>
像这样创build一个属性文件
type=javax.sql.DataSource driverClassName=org.gjt.mm.mysql.Driver url=jdbc:mysql://localhost/testdb username=user_name password=password
把Simple-JNDI和一个jndi.properties文件放在你的类path中。 然后照常访问你的数据源。
更多关于Simple-JNDI的信息可以在这里find。