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:
Next I create the business layer interface:Code:package test.model; public class User { private String username; private String password; //Getters/Setters... }
Then an implementation for the business layer:Code:package test.service; import test.model.User; public interface UserService { public User login(String username, String password); }
So the business layer implementation depends on the persistence layer, so next I create the persistence layer interface: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... }
And then I create a mock implementation:Code:package test.dao; import test.model.User; public interface UserDAO { public User getByUsername(String username); }
Eventually I would have a real DAO implementation with hibernate or something. Now I create the test for the service layer: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; } } }
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.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) {} } }
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?


Reply With Quote