Builder pattern and hierarchy of information in unit testing

By on May 26, 2012 in eng |

Share On GoogleShare On FacebookShare On Twitter

Builder pattern could help us make the code for preparing testing data more compact and readable. What I personally like is that the pattern could also be used to organize hierarchy of information in a test method.


Let’s look at the contrived example below.

@Test(expected=OverlappingScheduleEntryException.class)
public void anOwnerCanNotHaveTwoOverlappingScheduleEntry() {
		
	DaySchedule schedule = new DaySchedule();
	ScheduleEntry entry1 = new ScheduleEntry();
	entry1.setOwner("Chatchai");
	entry1.setStartTime( new DayTime("10:0:0") );
	entry1.setEndTime( new DayTime("11:0:0") );
	schedule.addEntry(entry1);
		
	ScheduleEntry entry2 = new ScheduleEntry();
	entry2.setOwner("Chatchai");
	entry2.setStartTime( new DayTime("10:30:0") );
	entry2.setEndTime( new DayTime("12:0:0") );
	schedule.addEntry(entry2);
	
	schedule.setActive();
	
	scheduleServices.saveSchedule(schedule);
}
	
public void overlappingIsCalculatedFromEntriesInTheSameDaySchedule() {
	DaySchedule schedule1 = new DaySchedule();
	ScheduleEntry entry1 = new ScheduleEntry();
	entry1.setOwner("Chatchai");
	entry1.setStartTime( new DayTime("10:0:0") );
	entry1.setEndTime( new DayTime("11:0:0") );
	schedule1.addEntry(entry1);
	schedule1.setActive();
		
	DaySchedule schedule2 = new DaySchedule();
	ScheduleEntry entry2 = new ScheduleEntry();
	entry2.setOwner("Chatchai");
	entry2.setStartTime( new DayTime("10:30:0") );
	entry2.setEndTime( new DayTime("12:0:0") );
	schedule2.addEntry(entry2);
	schedule2.setActive();
		
	assertEquals("Schedule is saved", true, scheduleServices.saveSchedule(schedule1) );
	assertEquals("Schedule is saved", true, scheduleServices.saveSchedule(schedule2) );
}

Both test cases are already concise and readable but I just don’t like how the testing data preparation is present at the same level as the testing step. If the preparation is complex then it will be hard to know which part is for set up the data and which part is the testing step.


It’s even more problem for me when I need to scan through many test cases to see if there is any case I have missed or to find a particular test scenario. I just want to take a quick look at each case and get a rough idea on what it is doing. Technically speaking, I just want to know how the dependencies of the class under test are interacting with testing data not how each testing data is set up. I could use builder pattern to write my test cases as below.

@Test(expected=OverlappingScheduleEntryException.class)
public void anOwnerCanNotHaveTwoOverlappingScheduleEntry2() {
		
	DaySchedule schedule = new DayScheduleBuilder()
					.aDaySchedule()
					.withEntry( new ScheduleEntryBuilder()
						.aScheduleEntry()
						.ofOwner("Chatchai")
						.startAt( new DayTime("10:0:0") )
						.endAt(new DayTime("11:0:0") ) 
						.build() ) 
					.withEntry( new ScheduleEntryBuilder()
						.aScheduleEntry()
						.ofOwner("Chatchai")
						.startAt( new DayTime("10:30:0") )
						.endAt(new DayTime("12:0:0") ) 
						.build() ) 
					.setActive()
					.build();
		
	scheduleServices.saveSchedule(schedule);
}
	
public void overlappingIsCalculatedFromEntriesInTheSameDaySchedule2() {
	DaySchedule schedule1 = new DayScheduleBuilder()
					.aDaySchedule()
					.withEntry( new ScheduleEntryBuilder()
						.aScheduleEntry()
						.ofOwner("Chatchai")
						.startAt( new DayTime("10:0:0") )
						.endAt(new DayTime("11:0:0") ) 
						.build() ) 
					.setActive()
					.build();
		
	DaySchedule schedule2 = new DayScheduleBuilder()
					.aDaySchedule()
					.withEntry( new ScheduleEntryBuilder()
						.aScheduleEntry()
						.ofOwner("Chatchai")
						.startAt( new DayTime("10:30:0") )
						.endAt(new DayTime("12:0:0") ) 
						.build() ) 
					.setActive()
					.build();
		
	assertEquals("Schedule is saved", true, scheduleServices.saveSchedule(schedule1) );
	assertEquals("Schedule is saved", true, scheduleServices.saveSchedule(schedule2) );
}

When the set up of testing data is aligned on the right of the screen like this, I feel like the expressions that have been indented to the right are sub-steps of the expression on the left. I can just focus on the left of the second test case, look from top to bottom and see there are two schedules being setup and the both of them are saved by the services at the last two line. Combining with the test method name, I can get a rough idea of what the test is trying to do.

The testing data preparation itself could involve many steps. Builder pattern also help us organize the code to reflect the hierarchy of testing data. It’s easy to see that the first test contains one schedule with two entries while the second test contains two schedules each with an entry.