Unit Tests

Now that you know what unit tests are and you know the basics of the JUnit framework, we can show you how to implement tests so that you can be sure they accurately test all aspects of your code in an unobtrusive way.

It is important to realize that even though we are developing the application using Spring and we are taking advantage of all its DI and Proxy features, we still need to make sure that the classes that implement our application's interfaces work correctly on their own. This is why we do not create the Spring application context, but instead manually create instances of the tested classes and set their dependencies. The dependencies are mock implementations of the dependency interfaces. Let's take a look at Listing A-3, which shows how we implement the tests for the DefaultBlogManager implementation of the BlogManager interface.

Listing A-3: AuditTest Unit Test

image from book
package com.apress.prospring.business;      import java.util.Date; import java.util.List;      import junit.framework.TestCase;      import com.apress.prospring.data.CommentDao; import com.apress.prospring.data.EntryDao; import com.apress.prospring.domain.Comment; import com.apress.prospring.domain.Entry; import com.apress.prospring.domain.User;      public class AuditInvokedTest extends TestCase {     private DefaultBlogManager bm = new DefaultBlogManager();     private CommentDao commentDao = new MockCommentDao();     private EntryDao entryDao = new MockEntryDao();     private MockAuditService auditService;          public AuditInvokedTest() {         bm.setCommentDao(commentDao);         bm.setEntryDao(entryDao);     }          public void setUp() {         auditService = new MockAuditService();         bm.setAuditService(auditService);     }          public void testSaveEntry() {         bm.saveEntry(new Entry());         performAssert();     }          public void testSaveComment() {         bm.saveComment(new Comment(), new User());         performAssert();     }          private void performAssert() {         assertEquals("The Audit Service was not invoked", 1,              auditService.callCount);     }          private class MockAuditService implements AuditService {         private int callCount = 0;                  public void writeAuditMessage(String data, User user) {             callCount++;         }              public void purgeAudit(Date oldestDate) {                      }                  public int getCallCount() {             return callCount;         }     }          private class MockCommentDao implements CommentDao {         public List getByEntry(int entry) {             return null;         }         public Comment getById(int commentId) {             return null;         }         public void save(Comment comment) {         }         public void delete(int commentId) {         }     }          private class MockEntryDao implements EntryDao {         public List getAll() {            return null;         }         public void save(Entry entry) {         }         public void delete(int entryId) {         }         public Entry getById(int entryId) {             return null;         }         public List getMostRecent(int count) {            return null;         }     }      } 
image from book

If this test case succeeds, we have proof that the DefaultBlogManager calls the writeAudit() method of the AuditService for each operation that modifies an entry. The DefaultBlogManager requires three dependencies—EntryDao, CommentDao, and AuditService—and we provided mock implementations for each of these interfaces. We do not need to worry about the EntryDao and CommentDao mock implementations, but we do check that the AuditService.writeAudit() implementation gets called when DefaultBlogManager.saveEntry() or DefaultBlogManager.saveComment() is called. This test successfully proves that the DefaultBlogManager performs the necessary auditing functions and yet the test does not require any other external resources. We can run it as many times as we want and still the test succeeds or fails consistently. The result of running this unit test from Eclipse is shown in Figure A-1.

image from book
Figure A-1: Unit tests in Eclipse

Writing all the mock classes manually can mean a lot of repetitive work. This is where EasyMock (www.easymock.org/) comes it. It is a framework that allows you to create mock classes automatically. You can even add behavior to the generated mock classes. Let's take a look at Listing A-4, which shows the refactored AuditInvokedTest class that uses the EasyMock library to generate the mock classes.

Listing A-4: AuditInvokedTest Using EasyMock

image from book
package com.apress.prospring.business;      import org.easymock.MockControl;      import junit.framework.TestCase;      import com.apress.prospring.data.CommentDao; import com.apress.prospring.data.EntryDao; import com.apress.prospring.domain.Comment; import com.apress.prospring.domain.Entry;      public class AuditInvokedTest2 extends TestCase {          private DefaultBlogManager bm = new DefaultBlogManager();          private MockControl auditServiceControl;     private AuditService auditService;          public void setUp() {         CommentDao commentDao;         EntryDao entryDao;              entryDao = (EntryDao)              MockControl.createControl(EntryDao.class).getMock();         commentDao = (CommentDao)              MockControl.createControl(CommentDao.class).getMock();              auditServiceControl = MockControl.createControl(AuditService.class);         auditService = (AuditService) auditServiceControl.getMock();              bm.setAuditService(auditService);         bm.setCommentDao(commentDao);         bm.setEntryDao(entryDao);     }          public void testSaveEntry() {         Entry entry = new Entry();          auditService.writeAuditMessage("Entry " + entry + " saved", null);         auditServiceControl.replay();         bm.saveEntry(entry, null);     }          public void testSaveComment() {         Comment comment = new Comment();         auditService.writeAuditMessage("Comment " + comment + " saved.", null);         auditServiceControl.replay();         bm.saveComment(comment, null);     }      }
image from book

The code in the setUp() method creates mock implementations for the EntryDao and CommentDao interfaces. Because we do not need to include any behavior in the mock classes, we do not have to store the MockControl object returned by the MockControl.createControl() method.

However, we actually need to test that the writeAuditMessage() method gets called on the AuditService, but at the same time, we do not want to manually create a mock implementation for it. In the testSaveEntry() and testSaveComment() method, we call the writeAuditMessage() method on the generated mock class with the expected arguments and then we call auditServiceControl.replay(). Doing this actually makes EasyMock check to make sure that the writeAuditMessage() method gets called and that the parameters passed to it are set to the expected values.

As you can see, using EasyMock is a very convenient way to generate mock implementations of your interfaces, and doing so allows for a certain level of control over the behavior of the generated mock classes.

Sometimes we need to test the components' links to the external components, which means that we need to write integration tests.



Pro Spring
Pro Spring
ISBN: 1590594614
EAN: 2147483647
Year: 2006
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net