0

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:

  • I need an instance of UserDao inside factory just to be able to call instantiate method (which is in fact copy constructor, but i need it to be a method to be able to use polymorphism to choose between UserDaoImpl or UserDaoMock) to create another object of UserDao that will be associated with transaction (depending on runtime type it will be instantiate method of either UserDaoImpl or UserDaoMock).

    public class UserDaoFactory {

     private static UserDaoFactory instance;
     private UserDao exampleUserDao;
    
     public static UserDaoFactory getInstance() {
         if(instance == null) {
             instance = new UserDaoFactory();
             // By default we initialize factory with example of real UserDao.
             // In test will be set to example of UserDaoMock with setExampleUserDao method.
             instance.setExampleUserDao(new UserDaoImpl(true));
         }
         return instance;
     }
    
     public UserDao getUserDao() {
         return exampleUserDao.instantiate(true);
     }
    
     public void setExampleUserDao(UserDao userDao) {
         this.exampleUserDao = userDao;
     }
    

    }

In UserDao:

  • I need both instantiate method (which in fact is copy constructor).

  • Despite that i also need public constructor to initialize factory with example.

  • Constructor should have parameter that tells whether it is example or not (whether to start transaction or not).

      public final class UserDaoImpl implements UserDao {
    
          private DataSource ds;
          private Connection conn;
    
          public UserDao instantiate(boolean startTransaction) {
              if(startTransaction) {
                  return new UserDaoImpl(true);
              } else {
                  return new UserDaoImpl(false);
              }
          }
    
          public UserDaoImpl(boolean initiate) {
              if(initiate) {
                  // DB connection initialization and start of the transaction
              } else {
                  new UserDaoImpl();
              }
          }
    
          private UserDaoImpl() {
          }
    
          @Override
          public void terminate(boolean commit) {
              // End of the transaction and DB connection termination
          }
    
          // Other interface methods implementation ommited
    
      }
    

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.

3 Answers 3

2

Use a proper mock framework like PowerMock or EasyMock (or both) or Mockito and do concrete unit testing for the class implementation. Do not create a UserDaoMock class implementation because then you're not covering the code in UseDaoImpl class, which is one of the advantages of unit testing.

Sign up to request clarification or add additional context in comments.

7 Comments

Oh, sorry. That wasn't clear enough. Code under test is my Struts Action that's why i need to mock UserDao.
Then mock your interface to make it return the necessary data using a proper mock framework.
My problem is not in generating mock by interface, but in dispatching between real object and mock. Fabric and extracted interface should allow to make transparent to Action whether it receives real UserDaoImpl or UserDaoMock, but i'm failing to implement such design. Main problem is that i need "example" of the object in fabric to be able to allocate another objects. If UserDao was singleton this would solve the problem but in this case interaction with the DB will be a bottleneck of the application.
@yaromir when using a proper mock framework (as stated 3 times now) you don't need to use this fabric at all. You will need only few lines of code to create an instance of UserDaoImpl where its methods return prepared data for testing purposes.
@yaromir also, your UserDaoImpl class MUST NOT (yes, remember it well) be a singleton.
|
1

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);
    }
}

5 Comments

A solution mocking HelloWorldDAO is simpler, in that it doesn't require the newHelloWorldDAO method in the class under test. Also, it would allow HelloWorldAction to be a final class.
Thanks a lot!!! Exactly what i've been searching for. @Rogério Mock can be created either by implementing same interface as real object or by subclassing from the real object. If you're choosing subclassing way you have to allow inheritance anyway.
@yaromir The kind of mocking I meant is completely general: it applies equally (using the same mocking API) to interfaces, abstract classes, concrete classes, final classes, enums, etc. In the case of mocking a class, no subclass needs to be created, as that is just one particular technique.
@Rogério Your points are legitimate, though it's difficult to contest the simplicity of this solution when a complete clear working example requires so few lines of code with no additional dependencies.
@gknicker Well, the solution I described requires less lines of code. It is simpler, according to the definition of simplicity described in the first paragraph of the second page in that article.
1

You can unit test the LoginAction class from the gist as shown in the following test, which uses the JMockit mocking library:

public class LoginTest
{
    @Tested LoginAction action;
    @Mocked ActionMapping mapping;
    @Mocked HttpServletRequest request;
    @Mocked HttpServletResponse response;
    @Capturing UserDao userDao;

    @Test
    public void loginUser()
    {
        final String username = "user";
        final String password = "password";
        LoginForm form = new LoginForm();
        form.setUsername(username);
        form.setPassword(password);

        final ActionForward afterLogin = new ActionForward("home");

        new Expectations() {{
            userDao.checkCredentials(username, password); result = true;
            mapping.findForward("successful-login"); result = afterLogin;
        }};

        ActionForward forwardTo = action.execute(mapping, form, request, response);

        assertSame(forwardTo, afterLogin);
        new Verifications() {{ userDao.terminate(true); }};
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.