tags:

views:

433

answers:

4

I am trying to write some test cases for my DAO classes in a J2EE applications. Methods in my DAO classes try to get connection to the Database based on a JDBC URL (which is on the app server). So from the front end if I click bunch of stuff and make the DAO trigger it runs fine. However, when I write tests cases for the DAO and the DAO object calls the method then it is not able to get the connection to the database. I think since the JDBC resource is on the App server that is why it is not working from the test class.

because of this when I run my tests instead of pass or fail..it returns bunch of errors.

Has someone encountered this issue? what can I do to overcome this?

Example:

public class DBConnectionManager {
   public static final String DB_URL = "jdbc/RSRC/my/connection/mydb"
   public Connection getconnection ()
   {
     DataSource ds = ServiceLocator.getInstance().getDataSource(DB_URL);
     return ds.getconnection();
   } 
}
public class MyDAO extends DBConnectionManager {
    publci SomeBean getContents (String id)
    {
        Connection con = getConnection();
        CallableStatement cs = con.prepareCall("{call myStorProc(?)}");
        cs.setString(1, id);
        ...
        //code to call resultset and retrieve SomeBean goes here
        ..
        return SomeBean;                
    }
}
public class MyTests extends TestCase {
    public testGetcontents ()
    {
        MyDAO myd = new MyDAO ();
        SomeBean smb = myd.getContents("someparm");
        assertEquals (5, smb.getSomeVal());
    }
}

Should I be doing something extra in my testcase...? if so what?

EDIT:

error I get is:

java.lang.NoClassDefFoundError: com/iplanet/ias/admin/common/ASException
        at java.lang.ClassLoader.defineClass1(Native Method)
A: 

It could be a permissions issue on the database you're trying to access. What errors are you getting?

One useful way for testing database access is to create a clean, local "test" version of your database as part of your test harness. Before you run your tests, use scripts to create a local copy of the database with all the pertinent data, then run your tests against that, rather than the remote server.

People may argue that testing against a database in a unit test is not truly a unit test, since it has an external dependency. If you're able to refactor your DAO classes, you can make it so the actual data source is injectable through some interfaces. In your test code, you'd inject a "mock" data source which provides your test data in some in memory format, then in production, you'd use/inject the actual database source classes. If you can hide the external (non-business code related) parts of your DAO behind interfaces, you can use mocks in your unit tests to test more of your functionality, rather than the actual data access.

Andy White
actually point of me writing this test case is other than just testing for data access. I asked this question a week back. this might tell you what i'm really trying to do http://stackoverflow.com/questions/1690401/need-suggestions-on-getting-started-with-junit. Furthermore, the errors I am getting are like '../logs/myapp.log' the system cannot find the path specified. All these errors are coming because i am not coming directly from web tier. is there a way to 'mock' the test such a way so it seems like they are coming from web tier...
Omnipresent
looks like it IS security problems. java.lang.NoClassDefFoundError: com/iplanet/ias/admin/common/ASException
Omnipresent
+1  A: 

Test well your ServiceLocator first. As you mentioned, the problem is probably because the datasource is declared on the server. Here the "bunch of errors" should be helpful, as of whether the problem is in acquiring the DataSource, or the Connectiion itself. What database are you using? Can you logon to it from your machine from console? If not - configure it so that your host is allowed.

Bozho
+2  A: 

Your DAO has a JNDI lookup string hard wired into it. Unless you have a JNDI lookup service available, it won't be able to get a connection.

I don't think a DAO should be responsible for acquiring a database connection. This design won't allow you to set transactions for a unit of work, because a DAO can't know if it's part of a larger unit of work.

I'd recommend passing the connection into the DAO, perhaps into its constructor. That way a service layer can establish appropriate transaction boundaries if there's more than one DAO in a single unit of work.

This design will have the added benefit of making it possible for your application to use its JNDI resource appropriately and your test to get its connection from a DriverManager, without having to use a JNDI lookup. You have two different sources for acquiring the DataSource or Connection - one for the app and another for the test.

UPDATE:

Here's what I mean, expressed in your code:

public class DBConnectionManager 
{
    public static final String DB_URL = "jdbc/RSRC/my/connection/mydb"

    public Connection getConnection (String jndiLookup)
    {
        DataSource ds = ServiceLocator.getInstance().getDataSource(jndiLookup);

        return ds.getconnection();
    } 

    public Connection getConnection(String driver, String url, String username, String password)
        throws ClassNotFoundException, SQLException
    {
        Class.forName(driver);

        return DriverManager.getConnection(url, username, password);
    }
}

public class MyDAO 
{
    private Connection connection;

    public MyDao(Connection connection)
    {
        this.connection = connection;
    }

    public SomeBean getContents (String id)
    {
        CallableStatement cs = this.connection.prepareCall("{call myStorProc(?)}");
        this.connection.setString(1, id);

        //code to call resultset and retrieve SomeBean goes here

        return someBean;                
    }
}

You show nothing about closing resources properly or transactions. Judging by this code, you'll be in trouble on both counts. I'd think carefully about your implementation.

I'll recommend Spring JDBC to you. You can write your DAOs in Spring without rewriting your whole app.

I'll also point out that you might also be looking at generics: Don't Repeat The DAO.

duffymo
thanks I like the design where connection is passed to the DAO. But then lets say you have some DAO helper class which actually calls the DAO...will that helper class pass connection to the DAO? I dont think right now I can change the design...guess I will have to drop the awesome testing ideas >_<. Using your design though, when i made object of the DAO...in the constructor I could pass connection acquired from DriverManager...
Omnipresent
can you suggest a way so that getContents() method in my DAO becomes aware whether it got called from the JUnit method. If it is...then it will take connection from driver manager...
Omnipresent
No, your DAO should not know whether it's being tested or used in the app. Pass in the connection from somewhere else.
duffymo
based on the code you provided...connection variable inside MyDAO class would never get populated (unless i'm reading it wrong?). Lets say I initiate MyDAO myd = new MyDAO(); then where is the call to getConnection (in DBConnectionManager) gets made?
Omnipresent
Yes, you are reading it wrong. There IS no default constructor, only one that takes a Connection passed in as parameter. You cannot create a DAO without a Connection the way I wrote it. That's the way Java works - you only get a default constructor from the compiler if you don't write a constructor.
duffymo
can believe I overlooked that. so based on your example it would work like DBConnectionManager dbm = new DBConnectionManager(); MyDAO myd = new MyDAO (dbm.getConnection("some/jndi"));
Omnipresent
That's one way. Another is MyDAO myDao = new MyDAO(dbm.getConnection("driver", "url", "username", "password"); See how your test class can work now?
duffymo
yup definitely notice that now. since you know my history and what I'm trying to achieve (compare results of two DAO's one old {in this question} with one new). Could you please see if you can give me some help on this question: http://stackoverflow.com/questions/1711566/is-this-possible-to-do-with-ibatis-spring
Omnipresent
A: 

Where I work our DAOs have an injectable connection (via constructor injection), and we unit test against a mock connection. To test the code in the DAO, we pass in a mocked (usually using Mockito) connection, and set up expectations in our unit tests as to what methods will be called. This makes for somewhat noisy tests, as the tests look very similar to the code being developed, but it works for us.

Dan Watt
What's the point of a mock connection? If you're testing a DAO, why would you not want to connect to a real database? This is one use of mocks that has never made sense to me. Once it's working, then mock the DAO for its clients.
duffymo