如何使用 Spring 测试模拟的 JNDI 数据源?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5940895/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to test a mocked JNDI datasource with Spring?
提问by Marco
I am fairly new to Spring and wondering how to create JUnit tests that use a mocked datasource and how to use a JNDI context with that? Currently my application uses a JNDI context from tomcat to retrieve a connection and via that connection retrieves data from a database. So I guess I need to mock the JNDI calls and the data retrieval. Any good pointers on what the best way to tackle this would be great! Thanks a lot!
我对 Spring 相当陌生,想知道如何创建使用模拟数据源的 JUnit 测试以及如何使用 JNDI 上下文?目前,我的应用程序使用来自 tomcat 的 JNDI 上下文来检索连接,并通过该连接从数据库中检索数据。所以我想我需要模拟 JNDI 调用和数据检索。关于解决这个问题的最佳方法的任何好的指示都会很棒!非常感谢!
采纳答案by Roadrunner
I usually define my JNDI dependencies in seperate file, like datasource-context.xml:
我通常在单独的文件中定义我的 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>
So that in test resources I can create another file and define the test datasource however it suits me, like datasource-testcontext.xml:
这样我就可以在测试资源中创建另一个文件并定义适合我的测试数据源,例如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>
And then in my test class I use the test configuration of the datasource instead of production onethat depends on JNDI:
然后在我的测试类中,我使用数据源的测试配置而不是依赖于 JNDI的生产配置:
@ContextConfiguration({
"classpath*:META-INF/spring/datasource-testcontext.xml",
"classpath*:META-INF/spring/session-factory-context.xml"
})
public class MyTest {
}
If the data source is not defined in a separate file You can still stub the object returned by JNDI calls easily:
如果数据源未在单独的文件中定义,您仍然可以轻松地存根 JNDI 调用返回的对象:
- like this: Injecting JNDI datasources for JUnit Tests outside of a container
- or using classes in package
org.springframework.mock.jndi, ie.SimpleNamingContextBuilder(there's an example in the javadoc of this calass).
- 像这样:在容器外为 JUnit 测试注入 JNDI 数据源
- 或使用包中的类
org.springframework.mock.jndi,即。SimpleNamingContextBuilder(这个 calass 的 javadoc 中有一个例子)。
回答by prule
You can use SimpleNamingContextBuilder to make a jndi datasource available to your tests:
您可以使用 SimpleNamingContextBuilder 使 jndi 数据源可用于您的测试:
SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
builder.bind("java:comp/env/jdbc/mydatasource", dataSource);
builder.activate();
This isn't exactly mockingthe datasource, but it does make the datasource available via jndi for your tests.
这并不是完全模拟数据源,但它确实通过 jndi 使数据源可用于您的测试。
回答by Paul Croarkin
You can create your own mock DataSource by extending Spring's AbstractDataSource.
您可以通过扩展 Spring 的 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;
}
}
I'd separate the JNDI lookup of the connection from the rest of the code. Inject the DataSource into your Data Access Objects (DAOs) and use the MockDataSource for testing the DAOs.
我会将连接的 JNDI 查找与代码的其余部分分开。将数据源注入您的数据访问对象 (DAO) 并使用 MockDataSource 来测试 DAO。
回答by Hannes
You can allways create a beans.test.xml configuration, where you first reference the beans.xml, and then override the datasource configuration:
你总是可以创建一个 beans.test.xml 配置,你首先引用 beans.xml,然后覆盖数据源配置:
src/main/resources/beans.xml
src/main/resources/beans.xml
<!-- Database configuration -->
<import resource="beans.datasource.jndi.xml" />
src/test/resources/beans.test.xml
src/test/resources/beans.test.xml
<import resource="beans.xml" />
<import resource="beans.datasource.test.xml" />
JUnit Test Class:
JUnit 测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/beans.test.xml" })
public class ASRTests
{
...
}
In your jndi bean, declare the reference
在您的 jndi bean 中,声明引用
<jee:jndi-lookup expected-type="javax.sql.DataSource" id="mysqlDataSource" jndi-name="jdbc/mysql"/>
In your test bean, declare the datasource
在您的测试 bean 中,声明数据源
<bean id="mysqlDataSource" ...>
...
</bean>
Keep in mind to move the test datasource bean into test folder.
请记住将测试数据源 bean 移动到测试文件夹中。
回答by Mohit
Spring's org.springframework.jndi.JndiObjectFactoryBeanis best suited for JNDI lookups. As per its documentation, it allows injecting default values as well for spring based test cases.
Springorg.springframework.jndi.JndiObjectFactoryBean最适合 JNDI 查找。根据其文档,它还允许为基于 spring 的测试用例注入默认值。
Refer to the below spring configuration (named as spring-test-db-config.xml)
参考下面的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>
Add bean defined on other configuration file shall refer to dataSourceFromJndibean
添加在其他配置文件中定义的 bean 应参考dataSourceFromJndibean
<!-- START OF SERVICES -->
<bean class="com.sample.Service" >
<property name="dataSource" ref="dataSourceFromJndi" />
</bean>
The advantage of this approach is that you can keep 2 different DB configuration files - one for production and other for unit testing. Just import the correct one. Test configuration will contain a default object.
这种方法的优点是您可以保留 2 个不同的数据库配置文件 - 一个用于生产,另一个用于单元测试。只需导入正确的。测试配置将包含一个默认对象。
回答by Vilish
Java Config.....
Java配置.....
Junit Test Case
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());
}
}
}
回答by Holger Thurow
You can also use Simple-JNDI. It is an in-memory JNDI Implementation for working with JNDI contexts outside of a J2EE container. It lets you use the same bean definition file in production and test. Assume this is your bean definition in production:
您还可以使用 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>
Create a property file like this
创建一个这样的属性文件
type=javax.sql.DataSource
driverClassName=org.gjt.mm.mysql.Driver
url=jdbc:mysql://localhost/testdb
username=user_name
password=password
Put Simple-JNDI and a jndi.properties file with a little configuration in your classpath. Then access your datasource as usual.
将 Simple-JNDI 和一个带有少量配置的 jndi.properties 文件放在您的类路径中。然后像往常一样访问您的数据源。
回答by AGan
I recently encountered the problem of mocking JNDI DB resource for my JUnit test case. I dealt with created a separate DBStub class, that contains mocked javax.sql.DataSource and assign it to the Spring simple implementation of JNDI naming context builder SimpleNamingContextBuilder,
我最近遇到了为我的 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();
}
}
}
Extend this class to the actual JUnit test class would resolve the issue,
将此类扩展到实际的 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());
}
}

