Announcement Announcement Module
Collapse
No announcement yet.
Confusion about testing three layer and domain model Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Confusion about testing three layer and domain model

    Hi,
    I'm a java programmer who is now using TDD and junit. In my current project, we are using webwork/spring/hibernate trioes.And in architecture
    view, there is a web controller/ service / dao three layer logic separation. And there is another important layer called domain model. Sevice layer
    usually delegate to domain model to complete it's service and it should be a thin layer. My question is how to test the web && service && domain model.After i have tested a particular business logic in domain logic, do i need to test corresponding conditions in service layer and web
    layer.For example, in traditional auction application(citing caveatemptor application in book "Hibernate In Action"):

    Code:
    public class Item implements Serializable { 
        public Bid placeBid(User bidder, MonetaryAmount bidAmount, 
                            Bid currentMaxBid, Bid currentMinBid) 
                throws BusinessException { 
        
                // Check highest bid (can also be a different Strategy (pattern)) 
                if (currentMaxBid != null && currentMaxBid.getAmount().compareTo(bidAmount) > 0) { 
                        throw new BusinessException("Bid too low."); 
                } 
        
                // Auction is active 
                if ( !state.equals(ItemState.ACTIVE) ) 
                        throw new BusinessException("Auction is not active yet."); 
        
                // Auction still valid 
                if ( this.getEndDate().before( new Date() ) ) 
                        throw new BusinessException("Can't place new bid, auction already ended."); 
        
                // Create new Bid 
                Bid newBid = new Bid(bidAmount, this, bidder); 
        
                // Place bid for this Item 
                this.getBids.add(newBid);  
        
                return newBid; 
        } 
    }
    
    public class ItemManager { 
        private ItemDao itemDao; 
        public void setItemDao(ItemDao itemDao) { this.itemDao = itemDao;} 
        public Bid loadItemById(Long id) { 
            itemDao.loadItemById(id); 
        } 
        public Collection listAllItems() { 
            return  itemDao.findAll(); 
        } 
        public Bid placeBid(Item item, User bidder, MonetaryAmount bidAmount, 
                                Bid currentMaxBid, Bid currentMinBid) throws BusinessException { 
            item.placeBid(bidder, bidAmount, currentMaxBid, currentMinBid); 
            itemDao.update(item);     
       } 
    }
    In this application, i will write four tests for placing bid in domain model layer.
    Code:
    public class ItemTest extends TestCase {
    
      public void testPlaceBid() {
      // all is well
     //  no exception will be thrown
      }
       public void testPlaceBidTooLow() { 
      // test placing bid when bid is too low, and there will a exception thrown.
      }
    public void testPlaceBidInactive() { 
      // test placing  bid when auction is not active yet, and there will a exception thrown.
      }
      
    public void testPlaceBidInvalidDate() { 
      // test placing  bid when auction already ended, and there will a exception thrown.
      }
    }
    After i test domain model , do i need to test all the corresponding conditions in service, like this?

    Code:
    public class ItemManagerTest extends TestCase {
     
      public void testPlaceBid() {
      // normal
     //  no exception will be thrown
      }
       public void testPlaceBidTooLow() { 
      // test placing bid when bid is too low, and there will a exception thrown.
      }
    public void testPlaceBidInactive() { 
      // test placing  bid when auction is not active yet, and there will a exception thrown.
      }
      
    public void testPlaceBidInvalidDate() { 
      // test placing  bid when auction already ended, and there will a exception thrown.
      }
    }
    I think in both test cases, the tests is nearly same. And if do so, there will be many tests that look very same spreaded from domain model to service layer to web layer.But if i don't do that, it seems that there isn't sufficient tests for testing service api.

    I'm a newbie to tdd and junit so maybe this is a silly question for you.But anyway i need your help to clear my eyes.
    Thanks a lot!

  • #2
    Instead of applying the same tests to the service layer method try mocking the domain model object and test that the service layer is calling the correct method on the domain model.

    This seems like a pretty dumb test but in the end the only thing the service is doing is calling a method on another object.

    In most cases when developing service layers you end up with more course grain methods that are coordinating calls to many domain models. In this case you are going to be testing the coordination code instead of retesting the domain model.

    Comment


    • #3
      How do you do that, could you give me some example, please?
      I think there are two problems:
      The first is that domain model object won't be injected to service layer.Because of this, i don't know how to mock it.
      The second is that domain model object isn't like dao layer, where for every dao class , there is a dao interface.So, it is difficult to mock it too.

      Comment


      • #4
        Here is how I would test the ItemManager.placeBid method.

        Code:
        /**
         * Tests ItemManager.placeBid method.
         */
        public void testPlaceBid() {
        	
        	Item item = new Item();
        	ItemDAO mockIteamDAO = new MockItemDAO();
        	ItemManager itemManager = new ItemManager();
        	itemManager.setItemDAO(mockItemDAO);
        	
        	// Call placeBid, mind you you would use real objects instead of
        	// empty new instances.
        	
        	itemManager.placeBid(
        			item,
        			new User(),
        			new MonetaryAmount(),
        			new Bid(),
        			new Bid());
        			
        	// Test that the item.placeBid method was called by checking a new Bid
        	// was added to the Item.  You already test the validation and
        	// exception handling when testing the Item object so you do not need
        	// to test when testing the ItemManager
        	assertEquals(
        		"A new Bid wasn't added to the Item",
        		1,
        		item.getBids().size());
        		
        	// Test that the update method was called on the ItemDAO, remember the
        	// ItemManager is really using a mock implementation of the ItemDAO
        	assertTrue(
        		"Method update wasn't called on MockItemDAO",
        		mockItemDAO.getUpdateCallCount() == 1);
        		
        	// If you don't want to or make mock your DAOs then another way to test
        	// that itemDAO.update was called would be to load the item object and
        	// make sure it was updated with a new Bid.
        	
        } // testPlaceBid

        Comment


        • #5
          Thank you for your reply, lenzenc.
          Now i understand what you mean by "try mocking the domain model object".In your example, you didn't really mock it, you just call the domain model object instead of mocking it.
          In another way, i think we can really mock domain object, and for ItemMananger that's really a unit test.For example
          Code:
          // implement it using EasyMock
          public class ItemManagerTest extends TestCase {
           
            public void testPlaceBid() {
            
            	// class under test
            	ItemMananger mgr = new ItemManagerImpl();
            	
            	// just set up accessory fixtures
          	User aBidder = new User();
          	MonetaryAmount bidAmount = new MonetaryAmount();
          	Bid currentMaxBid = new Bid();
          	Bid currentMinBid = new Bid();
            		
            	// set up mock for dao
            	MockControl itemControl = MockClassControl.createControl(Item.class);
            	Item item = (Item)itemControl.getMock();
            	
            	// set up mock for domain model object
            	MockControl daoControl = MockControl.createControl(ItemDAO.class);
            	ItemDAO dao = (ItemDAO)daoControl.getMock();
            	
            	item.placeBid(aBidder, bidAmount, currentMaxBid, currentMinBid);
            	dao.update(item);
            	
           	itemControl.replay();
           	daoControl.replay();
           	
           	mgr.placeBid(item, aBidder, bidAmount, currentMaxBid, currentMinBid);
           	
           	itemControl.verify();
           	daoControl.verify();
           	
            }
          }
          This way we may need less concrete fixture objects and the test is really a unit test.But maybe there are some situations in which i can't mock domain object.

          Comment

          Working...
          X