$include_dir="/home/hyper-archives/boost-users/include"; include("$include_dir/msg-header.inc") ?>
From: Rush Manbert (rush_at_[hidden])
Date: 2006-09-14 14:01:59
Scott Meyers wrote:
> Rush Manbert wrote:
> 
>>In  a case like the example, where I know it might be hard to setup the 
>>object correctly, I'd rather have a constructor that took some bogus 
>>argument type that set the object up in "simulated success" mode, but 
>>only in my debug build.
> 
> 
> I'm glad you mentioned this, because my recent knowledge of unit testing 
> comes from books like Beck's "Test-Driven Development" and Feathers' 
> "Working Effectively with Legacy Code" as well as countless breathless 
> articles lauding the wonder of unit testing and showing how it's all 
> oh-so-simple.
LOL - "oh-so-simple" breaks down pretty quickly in any large system, 
doesn't it?
   My feeling is that it would often be convenient to have a
> special "test" interface, but I've never seen any discussion of doing 
> that.  I can imagine a couple of ways of doing this, one being to 
> literally have a different interface for debug builds, another to have 
> "test only" interface elements cordoned off somewhere by convention 
> (similar to Boost's use of namespaces named "detail").
I have done this in the past. Here's a real world example:
The application was for a storage virtualization controller. There were 
two separate processors running different software. One handled the 
virtualization and client services, while the other actually knew how to 
do I/O to the storage devices. My subsystem was a service that did 
copies of various flavors (entire logical units, etc.). Since it 
couldn't actually do any I/O, it could send messages to the other 
processor that created, started, stopped, etc. a copy utility task. In 
the real case, the utility would send events to my service to let it 
know how many blocks had been copied, if an error occurred, etc. There 
could be many instances of copy processes/utilities running concurrently 
and independently.
I had to test the top level service without any support from the copy 
utility task on the other processor. (You may wonder why. Let's just say 
that there were two separate groups that developed software for the 
different processors, and we had somewhat different ideas regarding 
testability. My group also had a version of our code that ran on Windows 
and I needed to be able to test it there.) This meant that I needed to 
simulate the event stream from the utility task. I also had to be able 
to force errors (again by simulating events).
Needless to say, this required a fairly extensive testing API, plus a 
notion within the objects that implemented the service that it could be 
running in "test mode". I think it took as much or more work to develop 
the test interface and the test drivers as it took to develop the 
service itself, but it was completely worth it. Especially when there 
was a problem and we needed to sort out whether it was "our" code or 
"their" code. ;-) Also, since I had this capability, the Windows version 
of the service could be driven by our management UI, and the copy 
processes would appear to make progress, stop, start, and complete. The 
management UI couldn't tell that they were "fake", so that group could 
use it to test their software. It was really cool, but the reasons that 
it was even possible was that a testing subsystem was built into our 
code, and we were required to have complete tests for our subsystems, 
and I had to consider how to test my subsystem from day one.
In fact, our system shipped with the test subsystem included. It was not 
readily accessible, of course, but was really useful in some cases where 
we needed to test something on an installed system. This sort of 
capability in the field can be a real saving grace in an embedded system.
> 
> 
>>EventLog::EventLog (bool dummyarg)
>>{   // This constructor sets us up in simulated success mode.
>>     #ifdef DEBUG	// or whatever you use
>>         m_simulateSuccess = true;
>>     #else
>>         throw something useful
>>     #endif
>>}
> 
> 
> Hmmm, I'd think this entire constructor would exist only in debug 
> builds, e.g.,
> 
>    class EventLog {
>    public:
>    #ifdef DEBUG
>      EventLog(bool dummyarg);
>    #endif
> 
> Unfortunately, conditional compilation is not without its own resultant 
> spaghetti code and concomitant maintenance headaches.
> 
Oh, sure. Get the error at compile time if you're going to go this way. 
The conditional compilation stuff is a definite problem to be avoided if 
possible. It's hard to avoid if you want to implement a really 
comprehensive test interface, but does seem less desirable in order to 
support "exploratory" programming (which I think was the original 
context here).
- Rush