如何使用 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 04:29:01  来源:igfitidea点击:

How to test a mocked JNDI datasource with Spring?

springtestingjunitmockingdatasource

提问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 调用返回的对象:

回答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();

https://fisheye.springsource.org/browse/spring-framework/spring-test/src/main/java/org/springframework/mock/jndi/SimpleNamingContextBuilder.java?hb=true

https://fisheye.springsource.org/browse/spring-framework/spring-test/src/main/java/org/springframework/mock/jndi/SimpleNamingContextBuilder.java?hb=true

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 文件放在您的类路径中。然后像往常一样访问您的数据源。

More about Simple-JNDI is found here.

可以在此处找到有关 Simple-JNDI 的更多信息。

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