0

I'm doing my project using Tomcat 7, JSP, Servlets, Log4j and MySQL.

I've googled this question for hours with no proper answer.

How can I test my DAO's using DataSource and JUnit ?

I found this article recently, but don't know how to configure it for my purposes and don't sure if it's a good way of doing it.

I'm having the following DAO hierarchy: dao hierarchy

And I'm obtaining the DataSource in AbstractRepository in the following way:

...
protected final DataSource ds;
...
    public AbstractRepository() {
        DataSource dataSource = null;
        try {
            Context initContext = new InitialContext();
            dataSource = (DataSource) initContext
                    .lookup("java:/comp/env/jdbc/mydb");
        } catch (NamingException ex) {
            LOG.error("Cannot obtain a connection from the pool", ex);
        }
        ds = dataSource;
    }
...

Would you produce some code sample of the way to solve this problem for example for Entrant Repository Test?

marknorkin
  • 3,904
  • 10
  • 46
  • 82

3 Answers3

1

The code you have in AbstractRepository is pretty hard to test. The reason is the "smart constructor", that knows where he gets the datasource from. But you still have a number of options. You can either change your code (my choice) so that the constructor receives DataSource as a parameter, like

public AbstractRepository(DataSource dataSource){
  this.ds = dataSource;
}

so you will be able to initialize your repositories in Test environment with some test datasource (a mock?).

Or you can use some mocking Framework, e.g. EasyMock, to create a partial mock of the Repository you want to test. EasyMock creates partial mocks of classes without calling any constructor by default. Then you could either mock the getDataSource() method (I hope you have one, that is used everywhere you need to access the datasource?) to return the test datasource, or you can set the final ds field using Reflections (the worse choice, I suppose).

AGV
  • 414
  • 3
  • 7
  • the reason I did empty constructor is that children can override the DataSource if they needs, but if child uses the same database it can use the parent DataSource. Though I agree that it pretty much hard to test. My goal for this moment is to test it using JUnit (without using another testing framework) – marknorkin Feb 07 '15 at 12:21
  • and if I implement your proposed constructor, does it means that every child also need to create such construcotor with DataSource parameter ? Maybe is it better to pass a Connection ? – marknorkin Feb 07 '15 at 17:33
  • if the only declared constructor is `AbstractRepository(DataSource)` constructor, then each subclass will have to explicitly call `super(DataSource)`. In its constructor(s). I would advise to provide `MyRepository(DataSource)` constructor in every `MyRepository` extending `AbstractDatasource`.Your repositories does not need to know where the `DataSource` comes from. – AGV Feb 07 '15 at 18:44
  • To pass a `Connection` as a parameter to `AbstractRepository` construcotr is a bad idea. A connection should be closed after each transaction (if the `DataSource` is a pool of connections, the connection can be reused). That means that you will have to create a new instance of your `Repository` classes, each time you want to write or read an entity from the DB, cause you pass the connection to `AbstractRepository` constructor. – AGV Feb 07 '15 at 18:48
  • I've created a DataSource Factory.And also add costructors in each repository with DataSource parameter. But for a moment my DAO's created every time i need it in Controller. Should I re-design it ? Which design then is preferable ? – marknorkin Feb 07 '15 at 19:00
  • I recommend you to redesign. As (almost) always in software architecture, there's no perfect solution (design) that suite all needs. I prefer to make DAOs singletons (past 10 years with some dependency injection framework). They should be stateless (then they could be even reused by different threads). In your case in each `create()` or `update()` method you will start a new connection, execute a statement and close connection at the end. Don't forget to use `finally`. The connection whould be closed even if something went wrong. – AGV Feb 07 '15 at 19:10
  • as I'm not using any DI framework and so reinventing the wheel - would you give me a link a code sample how to implement dao's as singletones ? – marknorkin Feb 07 '15 at 19:18
  • Even better is to manage connection outside of DAOs. I.e. your `Repository` classes does not know anything about `DataSource` and do not open and commit/rollback connections. They should become a `Connection` object as a parameter to each `create()`, `update()`, or `find()`. The connections are started, commited or rollbacked by some `Service`, `TransactionManager` or `ConnectionManager`. – AGV Feb 07 '15 at 19:19
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/70483/discussion-between-alexey-gromov-and-mark). – AGV Feb 07 '15 at 19:23
1

The problem is that your code gets its dependency directly from the source. The advice I provided your previous question is similar to the advice from Alexey Gromov, but if for some reason you cannot change your code then you would need to do the following in your unit test:

1) Create the sub contexts that would be created for you normally using your Java container (e.g. Tomcat, Jetty). THat can be done using the following commands which I got from your link (i verified they work):

    System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
            "org.apache.naming.java.javaURLContextFactory");
        System.setProperty(Context.URL_PKG_PREFIXES, 
            "org.apache.naming");  

    Context ic = new InitialContext();
    ic.createSubcontext("java:");
    ic.createSubcontext("java:/comp");
    ic.createSubcontext("java:/comp/env");
    ic.createSubcontext("java:/comp/env/jdbc");

2) You need to bind your mock object to the name that your code is expecting. That can be done using InitialContext#bind.

ic.bind("java:/comp/env/jdbc/DSTest", mock(DataSource.class)); 

Please note that mock function is provided by Mockito... you can pass what ever mock object you want.

In my actual code I have this:

        mysqlDs = (DataSource) initCtx.lookup("java:/comp/env/jdbc/DSTest");
        System.out.println("----mysqlDs = " + mysqlDs + "\n");

Which prints out:

----mysqlDs = Mock for DataSource, hashCode: 1926808117
Community
  • 1
  • 1
Jose Martinez
  • 11,452
  • 7
  • 53
  • 68
  • thank you for the answer! I can re-design my code. And I already create another constructor with DataSource parameter passed to it in each Repository. So in my tests I think I can just create MysqlDataSource instance and passed it to tested repository, but I'm getting the `NPE` when trying to run this tests. – marknorkin Feb 07 '15 at 19:26
  • Where are you getting the NPE and how are you creating the mock MysqlDataSource? – Jose Martinez Feb 07 '15 at 19:35
  • `MysqlDataSource ds = new MysqlDataSource(); ds.setServerName("localhost"); ds.setPort(3306); ds.setDatabaseName("mydb"); ds.setUser("root"); ds.setPassword("password"); userRepository = new UserRepository(ds);` I'm testing it with JUnit. That is how I create DataSource in `BeforeClass`. The NPE is throwing in create,update,delete, find methods. So I think I'm not properly obtaining a MysqlDataSource – marknorkin Feb 07 '15 at 19:42
  • Is the NPE when using the Connection? Personally I would not use a real MysqlDataSource and would instead use a mocked DataSource and also a mock Connection. – Jose Martinez Feb 07 '15 at 19:48
  • from my googling how to test controller and dao I've seen that most people recommend to do it with Mockito library. But for a moment I'm out of time and as don't know this framework I don't sure if I should start it now. From my code sample in above comment I don't see a problem why I should get any exception. – marknorkin Feb 07 '15 at 19:56
  • You might get an exception if the DataSource returns a null Connection. Without seeing the code its hard to tell. Mockito is easy to learn and you will fall in love. – Jose Martinez Feb 07 '15 at 20:03
  • I think I try Mockito now, because it's all got bad. Would you produce sample how to moch my datasource and connection ? – marknorkin Feb 07 '15 at 21:10
  • http://stackoverflow.com/questions/28388204/how-to-test-dao-methods-using-mockito – marknorkin Feb 07 '15 at 22:31
  • Yeah no problem I'll provide an answer later tonight. – Jose Martinez Feb 07 '15 at 23:02
0

With TomcatJNDI you can easily access the Tomcat JNDI environment from within your classes as when your application would run within Tomcat. TomcatJNDI leverages Tomcat's JNDI system and configures it by processing the original Tomcat configuration files. It's usage is simple, e. g.

TomcatJNDI tomcatJNDI = new TomcatJNDI();
tomcatJNDI.processContextXml(contextXmlFile);
tomcatJNDI.processWebXml(webXmlFile);
tomcatJNDI.start();

Thereafter you can lookup the DataSource as you are used to. More information can be found here.

Holger Thurow
  • 764
  • 4
  • 12