Working With Design Patterns: Chain of Responsibility
Listing 4: JUnit tests.
import static org.junit.Assert.*;
import org.junit.*;
public class ChainTest {
private Manager manager;
private VicePresident vp;
private CEO ceo;
private ExpenseReport report;
private static final int SMALL_AMOUNT = 100;
private static final int VP_AMOUNT = 7000;
private static final int CEO_AMOUNT = 207000;
@Before
public void amountPreconditions() {
assertTrue(SMALL_AMOUNT <= manager.getDollarLimit());
assertTrue(manager.getDollarLimit() < VP_AMOUNT &&
VP_AMOUNT <= vp.getDollarLimit());
assertTrue(vp.getDollarLimit() < CEO_AMOUNT);
}
@Before
public void createReport() {
report = new ExpenseReport();
}
@Before
public void createDefaultChain() {
manager = new Manager("Joe", false);
vp = new VicePresident("Jane");
ceo = new CEO("Zeus");
manager.setNextApprover(vp);
vp.setNextApprover(ceo);
}
@Test
public void approvedByManager() {
report.setTotalDollarAmount(SMALL_AMOUNT);
manager.handle(report);
assertApprovedBy(manager);
}
@Test
public void approvedByVPWhenOverManagerLimit() {
report.setTotalDollarAmount(VP_AMOUNT);
manager.handle(report);
assertApprovedBy(vp);
}
@Test
public void approvedByCEOWhenOverVPLimit() {
report.setTotalDollarAmount(CEO_AMOUNT);
manager.handle(report);
assertApprovedBy(ceo);
}
@Test
public void approvedIntlByIntlManager() {
report.setTotalDollarAmount(SMALL_AMOUNT);
report.setIsInternationalTravel(true);
manager.setCanApproveInternational(true);
manager.handle(report);
assertApprovedBy(manager);
}
@Test
public void approvedIntlByVPWhenManagerNotIntl() {
report.setTotalDollarAmount(SMALL_AMOUNT);
report.setIsInternationalTravel(true);
assertFalse(manager.canApproveInternational());
manager.handle(report);
assertApprovedBy(vp);
}
@Test
public void approvedByVPWhenManagerOutOfOffice() {
report.setTotalDollarAmount(SMALL_AMOUNT);
manager.setOutOfOffice(true);
manager.handle(report);
assertApprovedBy(vp);
}
@Test
public void rejectedWhenCEOOutOfOffice() {
report.setTotalDollarAmount(CEO_AMOUNT);
ceo.setOutOfOffice(true);
manager.handle(report);
assertSame(State.rejected, report.state());
}
@Test
public void approvedByPeerWhenManagerOutOfOffice() {
report.setTotalDollarAmount(SMALL_AMOUNT);
Manager peer = new Manager("Jim", false);
peer.setNextApprover(vp);
manager.setOutOfOffice(true);
manager.setNextApprover(peer);
manager.handle(report);
assertApprovedBy(peer);
}
private void assertApprovedBy(Approver approver) {
assertSame(State.approved, report.state());
assertSame(approver, report.getHandler());
}
}
The client working with the chain of responsibility doesn't know and doesn't really care which approver will finally handle the expense report. In this implementation, the employee submitting the report knows the initial approver—his or her manager. However, there's no reason that the client even needs to know this much information. A factory could produce an appropriate Approver implementation for the client.

Figure 1: Chain of Responsibility.
About the Author
Jeff Langr is a veteran software developer with over a quarter century of professional software development experience. He's written two books, including Agile Java: Crafting Code With Test-Driven Development (Prentice Hall) in 2005. Jeff has contributed a couple chapters to Uncle Bob Martin's upcoming book, Clean Code. Jeff has written over 75 articles on software development, with over thirty appearing at Developer.com. You can find out more about Jeff at his site, http://langrsoft.com, or you can contact him via email at jeff at langrsoft dot com.



Solid state disks (SSDs) made a splash in consumer technology, and now the technology has its eyes on the enterprise storage market. Download this eBook to see what SSDs can do for your infrastructure and review the pros and cons of this potentially game-changing storage technology.