Lights are not changed at initialization
Time is wrong, day is wrong, no lights are changed Day is right, time is wrong, no lights are changed Day is wrong, time is right, no lights are changed Day is right, time is right, the right light is turned on Day is right, time is right, the right light is turned off Schedule every day
Schedule a specific day Schedule all weekdays Schedule weekend days Remove scheduled event Remove non-existent event
Multiple scheduled events at the same time Multiple scheduled events for the same light Remove non scheduled light schedule
Schedule the maximum supported number of events (128) Schedule too many events
Figure 8.1: Light Scheduler test list
idea to make the core logic of the system testable. Now you have another good reason to introduce these layers in your code.
In the closing of the previous chapter, you saw three techniques for sneaking test doubles into the test runner, one of which was link-time substitution. We’ll use link-time substitution in this situation, because we want to completely eliminate the OS and hardware dependencies in the test executable.
We will also use the CppUTest test harness for the rest of the book.
CppUTest tests look very similar to Unity tests. You may want to look again at Section2.3, CppUTest: A C++ Unit Test Harness, on page44or AppendixC, on page332, for a refresher.
LIGHTSCHEDULERTESTLIST 147
8.1 Light Scheduler Test List
We’re developing light-scheduling features of a home automation sys-tem. The first cut of the test list is in Figure8.1, on the previous page.
The test list is roughly in the order we expect to implement the tests.
Don’t worry too much about getting the test order exactly right; you will change the order as you go. You will discover forgotten tests or find that an item in the test list is really multiple tests. Don’t spend too much time on the test list. If you try to get it perfect, you are definitely spending too much time on it.
8.2 Dependencies on Hardware and OS
Let’s see how we can design and test the light-scheduling portion of a home automation system. The component that handles light schedul-ing is called the LightScheduler, shown in context in Figure 8.2, on the following page. The LightScheduler has transitive dependencies on the hardware and the operating system (OS). If left unbroken, these depen-dencies mean that the light scheduler will be testable only in the target hardware.
The design works like this. The client of the LightScheduler is in the AdminConsolesubsystem. TheAdminConsoleinstructs theLightScheduler to turn on and off the lights at specific times during the week. Every minute, the LightScheduleris pinged through an OS callback from Time-Service. The ping triggers theLightSchedulerto check its internally main-tained schedule of light control actions. At the appropriate time, the LightSchedulertells theLightControllerto turn on or off a light by itsid. Each design element has focused responsibilities. The scheduler owns the overall application logic, whileLightControllerandTimeServiceinteract with hardware and OS.LightControllerandTimeServiceare parts of larger hardware and operating system abstraction layers.1
By following the dependency arrows, you can see that the LightSched-uler transitively depends on the hardware and OS. The dependencies could mean that LightScheduler can be tested only in the target. But with our separation of responsibilities, it’s easy to disconnect the prob-lem dependencies during test. Let’s look at how we can use the linker to break the transitive dependency on the hardware and OS.
1. Note that not all interface functions are shown in the UML diagrams, just a few representative functions. This is common for informal usage of UML.
Report erratum this copy is (P1.0 printing, April, 2011)
Download from Wow! eBook <www.wowebook.com>
LINK-TIMESUBSTITUTION 148
Time Service + GetTime()
+ SetPeriodicAlarm() Light
Scheduler +ScheduleTurnOn() +RemoveSchedule() +WakeUp()
Light Controller + On(id)
+ Off(id)
Hardware RTOS
<<anonymous callback>>
Admin Console
Figure 8.2: Initial light scheduler design
8.3 Link-Time Substitution
To break the dependencies on the production code, think of the collabo-rators only in terms of their interfaces. In Figure8.3, on the next page, we can see the separation of the interface from the implementation.
The interface is represented by the header file, and the implementation is represented by the source file, as we have discussed. LightScheduler is bound to the production code implementation at link time. Michael Feathers, author of Working Effectively with Legacy Code [Fea04], calls this a link seam. And at a seam we have flexibility.
The unit tests take advantage of the link seam by providing alterna-tive implementations forLightControllerandTimeService, as shown in Fig-ure8.4, on page150.
Though there are other ways to accomplish this, a good way to struc-ture the test build is to compile all production code into a library. Test doubles are left as object (.o) files. When building for test, the makefile explicitly links the test double object files before linking to the
produc-SPYING ON THECODEUNDERTEST 149
<<interface>>
Time Service + GetTime()
+ SetPeriodicAlarm() Light
Scheduler + ScheduleTurnOn() + RemoveSchedule() +WakeUp()
<<interface>>
Light Controller + On(id)
+ Off(id)
Model 42 Hardware RTOS
<<anonymous callback>>
Model 42 Light Controller
RTOS Time Service
<<implements>> <<implements>>
Admin Console
Figure 8.3: The light scheduler instructs collaborators through their interfaces.
tion code library. This allows the test doubles to override production code with same names. This is described in more detail in AppendixA, on page322.
8.4 Spying on the Code Under Test
The spy is on a covert operation. It intercepts the inputs destined for the production code, later providing it to the test case. As part of its covert mission, it may also feed return results to the client code, getting the CUT to do the test’s bidding. Very sneaky indeed.
After setting up the initial LightScheduler files (I use the shell scripts provided with CppUTest to create the initial files), write a test case that helps you envision the roles of theLightControllerSpy and FakeTimeSource
Report erratum this copy is (P1.0 printing, April, 2011)
Download from Wow! eBook <www.wowebook.com>
SPYING ON THECODEUNDERTEST 150
<<interface>>