如何使用Spring测试模拟的JNDI数据源?

时间:2011-05-09 18:27:28

标签: spring testing junit mocking datasource

我是Spring的新手,想知道如何创建使用模拟数据源的JUnit测试以及如何使用JNDI上下文?目前,我的应用程序使用tomcat中的JNDI上下文来检索连接,并通过该连接从数据库中检索数据。所以我想我需要模拟JNDI调用和数据检索。关于解决这个问题的最佳方法的任何好的指针都会很棒!非常感谢!

8 个答案:

答案 0 :(得分:34)

您可以使用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

这不完全是 mocking 数据源,但它确实可以通过jndi为您的测试提供数据源。

答案 1 :(得分:32)

我通常在单独的文件中定义我的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调用返回的对象:

答案 2 :(得分:4)

您可以通过扩展Sp​​ring的AbstractDataSource来创建自己的模拟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测试DAO。

答案 3 :(得分:2)

您可以始终创建beans.test.xml配置,首先引用beans.xml,然后覆盖数据源配置:

的src /主/资源/ beans.xml中

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

的src /测试/资源/ beans.test.xml

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

JUnit测试类:

@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"/>

在测试bean中,声明数据源

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

请记住将测试数据源bean移动到测试文件夹中。

答案 4 :(得分:1)

Spring的org.springframework.jndi.JndiObjectFactoryBean最适合JNDI查找。根据其文档,它允许为基于弹簧的测试用例注入默认值。

请参阅以下spring配置(名为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>

在其他配置文件上定义的添加bean应引用dataSourceFromJndi bean

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

这种方法的优点是您可以保留2个不同的DB配置文件 - 一个用于生产,另一个用于单元测试。只需导入正确的一个。测试配置将包含一个默认对象。

答案 5 :(得分:0)

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

}

答案 6 :(得分:0)

您也可以使用Simple-JNDI。它是一个内存中的JNDI实现,用于在J2EE容器之外使用JNDI上下文。它允许您在生产和测试中使用相同的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>

像这样创建一个属性文件

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

将Simple-JNDI和jndi.properties文件放在类路径中稍加配置。然后像往常一样访问您的数据源。

More about Simple-JNDI is found here.

答案 7 :(得分:0)

我最近遇到了为我的JUnit测试案例模拟JNDI DB资源的问题。我处理过一个单独的DBStub类,该类包含模拟的javax.sql.DataSource并将其分配给JNDI命名上下文构建器SimpleNamingContextBuilder的Spring简单实现,

public class DBStub {

@Mock
DataSource dataSource;

public DBStub() {
    try {
        MockitoAnnotations.initMocks(this);
        SimpleNamingContextBuilder builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();
        builder.bind("java:comp/env/jdbc/DataSource", dataSource);
    } catch (NamingException e) {
        fail();
    }

    } 
}

将此类扩展到实际的JUnit测试类将解决此问题,

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:application-context.xml" })
public class PricingOperationTest extends DBStub {

    @Autowired
    private JdbcTemplate template;

    @Test
    public void testDataSource() {
        assertNotNull(template.getDataSource());
    }
}