Announcement Announcement Module
Collapse
No announcement yet.
How do you create unit tests for layered applications? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • How do you create unit tests for layered applications?

    I am trying to figure out the best way to create unit tests for a web application that is separated into Presentation, Business and Persistence layers. I am working on a simple example of a user logging in with a username and password. So first I create a JavaBean to model a User:
    Code:
    package test.model;
    
    public class User {
    
        private String username;
        private String password;
    	
        //Getters/Setters...    
    }
    Next I create the business layer interface:
    Code:
    package test.service;
    
    import test.model.User;
    
    public interface UserService {
    
    	public User login(String username, String password);
    	
    }
    Then an implementation for the business layer:
    Code:
    package test.service.impl;
    
    import test.dao.UserDAO;
    import test.model.User;
    import test.service.UserService;
    import test.service.ex.ServiceException;
    import test.util.StringUtil;
    
    
    public class UserServiceImpl implements UserService {
    
        private UserDAO userDao;
        
        public User login(String username, String password) {
            User user = userDao.getByUsername(username);
            if(user == null) {
                throw new ServiceException("No user with username '"+username+"'");
            } else if(user.getPassword() == null || 
                    !user.getPassword().equals(StringUtil.toMD5(password))) {
                throw new ServiceException("Password does not match");
            }
            
            return user;
        }
    
        //Getters/Setters...
    }
    So the business layer implementation depends on the persistence layer, so next I create the persistence layer interface:
    Code:
    package test.dao;
    
    import test.model.User;
    
    public interface UserDAO {
    
        public User getByUsername(String username);
        
    }
    And then I create a mock implementation:
    Code:
    package test.dao.mock;
    
    import test.dao.UserDAO;
    import test.model.User;
    
    public class MockUserDAOImpl implements UserDAO {
    
        private User user = new User();
        
        public MockUserDAOImpl() {
            
            user.setUsername("user");
            //MD5 hash of "test"
            user.setPassword("098f6bcd4621d373cade4e832627b4f6");
            
        }
        
        public User getByUsername(String username) {
            if(user.getUsername().equalsIgnoreCase(username)) {
                return user;
            } else {
                return null;    
            }
        }
    
    }
    Eventually I would have a real DAO implementation with hibernate or something. Now I create the test for the service layer:
    Code:
    package test.service;
    
    import test.model.User;
    import test.service.ex.ServiceException;
    
    public class UserServiceTest extends BaseServiceTestCase {
    
    	private UserService service;
    	
        public UserServiceTest() {
            super();
            service = (UserService)context.getBean("userService");
        }
        
        public void testLogin() throws Exception {
            User user = service.login("user","test");
            assertEquals("user",user.getUsername());
        }
        
        public void testLoginInvalidUsername() throws Exception {
            try {
                User user = service.login("wrong","test");
                fail("Service Exception should have been thrown");
            } catch(ServiceException ex) {}
        }
    	
        public void testLoginIncorrectPassword() throws Exception {
            try {
                User user = service.login("user","wrong");
                fail("Service Exception should have been thrown");
            } catch(ServiceException ex) {}
        }	
        
        public void testLoginUsernameCaseSensitive() throws Exception {
            User user = service.login("uSeR","test");
            assertEquals("user",user.getUsername());
        }
        
        public void testLoginPasswordCaseSensitive() throws Exception {
            try {
                User user = service.login("user","tEsT");
                fail("Service Exception should have been thrown");
            } catch(ServiceException ex) {}
        }
        
    }
    BaseServiceTestCase takes care of looking up the context so that the userDao property of userService is set with an instance of MockUserDAOImpl. So now I have tests for my business layer.

    So my question is where do I go from here? I will need to create a controller in the presentation layer. How do I create a unit test for that? It will obviously depend on an implementation of UserService. So do I just use the same userService with the Mock userDao in the controller? Doesn't that make the test not really a unit test? Do I have to create a MockUserServiceImpl that doesn't depend on a dao? Creating and maintaining all these mock implementations is a lot of overhead, isn't it?

  • #2
    Consider using EasyMock and/or JMock to make your tests more modular.

    For controller testing, you might find this thread useful.

    Also, take a look at the petclinic sample and Spring test source tree (both in the distribution).

    Comment


    • #3
      I did take a look at the petclinic sample, but that only has integration tests for the DAO layer. I don't see any tests for the conroller. Plus the petstore doesn't really have a business layer, just one DAO (Clinic) that is called by the controller.

      I think you are right, dynamic mocks seem to be the way to go. I just read the chapter in Spring Live about using Dynamic Mocks, very helpful. It seems like what Matt Raible is saying is that you shold create 4 types of tests:

      Integration Tests for the DAO layer
      Unit Tests for the Business (Service/Manager/etc.) layer, with Mock DAOs
      Unit Tests for the Controllers, with Mock Services/Managers
      Integration Tests for the Views with jWebUnit or Canoo

      Comment

      Working...
      X