How to test a mocked JNDI datasource with Spring?

70,986

Solution 1

I usually define my JNDI dependencies in seperate file, like 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:

<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 one that depends on 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:

Solution 2

You can use SimpleNamingContextBuilder to make a jndi datasource available to your tests:

    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

This isn't exactly mocking the datasource, but it does make the datasource available via jndi for your tests.

Solution 3

You can create your own mock DataSource by extending Spring's 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.

Solution 4

You can allways create a beans.test.xml configuration, where you first reference the beans.xml, and then override the datasource configuration:

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 Test Class:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:/beans.test.xml" })
public class ASRTests
{
...
}

In your jndi bean, declare the reference

<jee:jndi-lookup expected-type="javax.sql.DataSource" id="mysqlDataSource" jndi-name="jdbc/mysql"/>

In your test bean, declare the datasource

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

Keep in mind to move the test datasource bean into test folder.

Solution 5

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,

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,

@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());
    }
}
Share:
70,986
Marco
Author by

Marco

Happy coder, system architect, software geek, cloud believer, tech guy, open-source enthusiasts, wannabe cooking chef, likes to travel, meet new people, share knowledge, and watch movies/series.

Updated on September 12, 2020

Comments

  • Marco
    Marco almost 4 years

    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!

  • Marco
    Marco about 13 years
    If I inject the datasource, would this not eliminate the need of a JNDI lookup?
  • Paul Croarkin
    Paul Croarkin about 13 years
    It could. There are a number of ways in Spring to get the DataSource. Once you have it, you can inject. Spring can read a DataSource from JNDI, though.
  • Grzegorz Oledzki
    Grzegorz Oledzki about 13 years
    I edited your answer to remove the indent of the first line. Now the syntax highlighting works. I hope you don't mind.
  • fastcodejava
    fastcodejava almost 12 years
    I did that, but I'm still getting exception Caused by: javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
  • fastcodejava
    fastcodejava almost 12 years
    I did that, but I'm still getting exception Caused by: javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
  • Roadrunner
    Roadrunner almost 12 years
    @fastcodejava You did what exactly? Used separate file for JNDI related configs? Created JNDI context in the test setup? Or used SimpleNamingContextBuilder?
  • Nicktar
    Nicktar over 5 years
    unfortunately the link to the oracle blog post seems dead. I cound not find an equivalend replacement...