javaunit-testingjdbcmockingdao

Java. Mock data access object


I've encountered problem with mocking data access logic.

I'm developing web application using JavaEE, Struts and my custom data access logic. In this application Struts Action operates with UserDao to retrieve User objects. Lifecycle of the UserDao object is tied to the JDBC transaction. Idea is that when Action creates UserDao object it starts JDBC transaction (necessary JDBC stuff is stored inside UserDao object), all invocations of UserDao methods operate in single JDBC transaction and then Action terminates UserDao object finishing transaction (with either commit or rollback).

The problem is that during tests of the Actions i need to mock this UserDao to make it return User objects with necessary test data.

The only solution i found so far is horrible. I've made following: splitted UserDao into UserDao interface and UseDaoImpl class that implements it. This interface will also be implemented by UserDaoMock that will be returning necessary test data. Next i need some factory that will return real UserDao (UserDaoImpl) in production run and mock (UserDaoMock) in test run. This factory should not depend from UserDaoMock type to make production code of the application independent from mock .class file. This results into horrible design.

Shortcomings:

In factory:

In UserDao:

Is there a way to make proper design for testability in this case?

Or, if not, maybe the cause of the problem is that i decided to tie UserDao lifecycle to JDBC transaction? What is possible alternatives? To make UserDao singleton? Won't this be an application bottleneck if all DB interaction will be done via single object.

Or, can you suggest another data access pattern that is more easy to mock?

Please, help. It is the most horrible design i did in my life.

Thanks in advance.


Solution

  • What you need is the simplest thing that could possibly work. Here is a Hello World example for just that.

    /* DAO with a data access method */
    public class HelloWorldDAO
    {
        public String findGreeting()
        {
            return "Hello from the database!";
        }
    }
    
    /* Struts Action class with execute method */
    public class HelloWorldAction implements Action
    {
        private String greeting;
    
        /* Uses indirection for DAO construction */
        @Override
        public String execute() throws Exception {
            HelloWorldDAO dao = newHelloWorldDAO();
            setGreeting(dao.findGreeting());
            return SUCCESS;
        }
    
        /* The simplest possible dependency injection */
        protected HelloWorldDAO newHelloWorldDAO() {
            return new HelloWorldDAO();
        }
    
        public String getGreeting() {
            return this.greeting;
        }
    
        public void setGreeting(String greeting) {
            this.greeting = greeting;
        }
    }
    
    /* Unit tests for HelloWorldAction */
    public class HelloWorldActionTest
    {
        @Test
        public void testExecute() throws Exception
        {
            final String expectedGreeting = "Hello Test!";
            String expectedForward = "success";
    
            HelloWorldAction testAction = new HelloWorldAction() {
                /* Override dependency injection method to substitute a mock impl */
                @Override
                protected HelloWorldDAO newHelloWorldDAO()
                {
                    return new HelloWorldDAO() {
                        @Override
                        public String findGreeting()
                        {
                            return expectedGreeting;
                        }
                    };
                }
            };
    
            String actualForward = testAction.execute();
            String actualGreeting = testAction.getGreeting();
    
            assertEquals("forward", expectedForward, actualForward);
            assertEquals("greeting", expectedGreeting, actualGreeting);
        }
    }