$include_dir="/home/hyper-archives/boost-users/include"; include("$include_dir/msg-header.inc") ?>
From: Peter Dimov (pdimov_at_[hidden])
Date: 2006-09-15 08:41:29
Scott Meyers wrote:
> David Abrahams wrote:
>> I'd much rather develop a library of mock objects for testing.  Okay
>> a conforming mock stream may be a little work to write, but you
>> write it once and you're done.
>
> I take this to mean that you have not found it
> necessary/desirable/useful to create separate test and client
> interfaces.  Okay.
>
> My understanding is that the ability to drop in mock objects requires
> programming to an interface that can be either a "real" object or a
> mock object, which in turn suggests using either a template parameter
> or a base class.  Suppose, for example, you have this:
>
>   class BigHonkinHairyClass {
>   ...                            // expensive to construct
>   };
>
> and you want to implement some function f that takes a
> BigHonkinHairyClass as a parameter.  The straightforward declaration
> would be
>
>   void f(BigHonkinHairyClass& bhhc);   // maybe const, doesn't matter
>
> But now it's hard to drop in a mock.  So I assume you'd modify the
> interface to be either
>
>   template<typename T>
>   void f(T& bhhc);               // T must more or less model
>                                  // BigHonkinHairyClass
>
> or this:
>
>   class BigHonkinHairyBase {
>   ...                            // interface to program against --
>   };                             // uses virtuals
>
>   class BigHonkinHairyClass: public BigHonkinHairyBase { ... };
>
>   class MockHonkinHairyClass: public BigHonkinHairyBase { ... };
>
>   void f(BigHonkinHairyBase& bhhc);
>
> Is that correct?  In other words, you'd come up with an interface that
> let clients do what they wanted but that was also mock-friendly for
> testing purposes?  That is, you'd take testability into account when
> designing your client interface?
This situation should be rare in well-designed code. Either f requires a 
BigHonkingHairyClass in order to work - by this I mean the precise semantics 
of BHHC - and it has to be tested using a BHHC; you can't substitute a mock 
that emulates a BHHC perfectly because it will be a BHHC itself.
Or, f doesn't really require a BHHC, and it should be rewritten in one of 
the two ways above. This allows client A to pass a BHHC and client B to pass 
something else. From the library design PoV, it doesn't matter whether 
client B is a test suite or just another module.
It is true that in many projects, the lower layers aren't designed as a 
proper library, since they don't have to serve arbitrary client code. In 
such a case, having a test suite as a second client can indeed lead to 
problems like the above. :-)
Another angle is that tests should test the behavior that is exercised by 
the application. If the application uses f with a BHHC, the tests should 
test f with a BHHC. Testing f with a mock can find some errors, but may 
easily miss others. This can be substituted by defining a rigorous interface 
for BHHCs and testing both f and BHHC against that interface, of course, 
which gets us back to one of the two refactorings given above. (But the 
f+actual BHHC test should still be part of the suite, IMO.)