Posted by cagataycivici on August 23, 2006
A simple layered architecture usually consists of a view layer, a service layer and domain layer. DAO’s are also placed at the domain layer together with the domain objects. Only the upper layer is aware of the lower layer and the interaction happens from top to bottom. A well known example might be JSF-Spring-Hibernate where spring beans are injected to JSF managed beans and these service level spring beans are used to interact with hibernate(HibernateTemplate).
Testing the DAO’s is fairly simple using the spring’s AbstractBlaBlaSomeFancyNameTestCases, actually the fun part is testing the service level business objects(in this case a spring bean). Assume we are trying to save a Customer object to the db and view layer makes a call to the service object. Later after doing a check, service bean makes a call to the DAO layer. Following stuff is needed;
* ICustomerDAO
* CustomerDAO
* IBusinessService
* BusinessService
* Customer
The whole idea is to do a simple check to see whether the customer is already saved or not. If the customer is already in db, a business rule exception is thrown.
ICustomerDAO
package domain;
public interface ICustomerDAO { public void save(Customer customer); public boolean isAlreadySaved(Customer customer); } |
|
CustomerDAO
package domain;
import java.util.List;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class CustomerDAO extends HibernateDaoSupport implements ICustomerDAO{
public void save(Customer customer){ getHibernateTemplate().save(customer); }
public boolean isAlreadySaved(Customer customer) { //Some algorithm to do the check here }
} |
|
IBusinessService
package service;
import exceptions.BusinessRuleException; import domain.Customer;
public interface IBusinessService { public void saveCustomer(Customer customer) throws BusinessRuleException; } |
|
BusinessService
package service;
import exception.BusinessRuleException; import domain.ICustomerDAO; import domain.Customer;
public class BusinessService implements IBusinessService{
private ICustomerDAO customerDAO;
public void saveCustomer(Customer customer) throws BusinessRuleException{ if(customer == null || getCustomerDAO().isAlreadySaved(customer)) throw new BusinessRuleException(); else getCustomerDAO().save(customer); }
public ICustomerDAO getCustomerDAO() { return customerDAO; }
public void setCustomerDAO(ICustomerDAO customerDAO) { this.customerDAO = customerDAO; }
} |
|
Finally the fun part, testing the BusinessService;
BusinessServiceTest
package service;
import org.jmock.Mock; import org.jmock.MockObjectTestCase;
import exception.BusinessRuleException;
import domain.ICustomerDAO; import domain.Customer;
public class BusinessServiceTest extends MockObjectTestCase {
private BusinessService businessService;
private ICustomerDAO customerDAO;
private Mock mockCustomerDAO;
protected void setUp() throws Exception { businessService = new BusinessService();
mockCustomerDAO = new Mock(ICustomerDAO.class); customerDAO = (ICustomerDAO)mockCustomerDAO.proxy(); businessService.setCustomerDAO(customerDAO); }
public void testTryingToSaveACustomerThatIsAlreadyInDBCausesBusinessRuleException() { mockCustomerDAO.expects(once()).method(“isAlreadySaved”).will(returnValue(true)); try { businessService.saveCustomer(new Customer()); fail(); }catch(BusinessRuleException businessRuleException) { //test passes } }
public void testTryingToSaveACustomerThatIsNullCausesBusinessRuleException() { try { businessService.saveCustomer(null); fail(); }catch(BusinessRuleException businessRuleException) { //test passes } }
} |
|
That’s all, the service object uses a DAO that actually hits the db. In order to test it, I’ve replaced it a mock version. The example is a simple case, but the idea remains same for more complex business rules. The good practice is to make these kind of checks at service layer instead of at DAO. Although I’ve used spring-hibernate as an example, the practice applies to all kind of architectures containing a business layer.
This entry was posted on August 23, 2006 at 9:15 am and is filed under Java.
.
You can follow any responses to this entry through the RSS 2.0 feed.
You can leave a response, or trackback from your own site.
August 23, 2006 at 10:16 am
Hi,
I agree with you, but personally I prefer using EasyMock (http://www.easymock.org) over JMock (basically because it allows compiler checks and refactoring on expected methods, which are not referenced simply by a string), and using a generic dao (http://forum.springframework.org/showthread.php?t=25160) instead of a single dao for each entity.
What do you think?
August 23, 2006 at 12:48 pm
Well, easymock vs jmock is subject to discussion. I usually use jmock just because I got used to it:). Generic DAO is also the approach I usually follow but sometimes I prefer domain specific daos when there are domain specific requirements to keep the generic dao simple and decoupled from entities.
April 29, 2008 at 7:05 pm
Wondering why you are not using spring Mock to test service level spring beans