home bbs files messages ]

Forums before death by AOL, social media and spammers... "We can't have nice things"

   comp.lang.c++.moderated      Moderated discussion of C++ superhackery      33,346 messages   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]

   Message 32,784 of 33,346   
   Ian Collins to Richard   
   Re: Unit Testing Frameworks (was Re: Sin   
   09 Jan 13 15:54:07   
   
   From: ian-news@this.is.invalid   
      
   Richard wrote:   
   > Ian Collins  spake the secret code   
   >  thusly:   
   >   
   >> [...] For unit testing, you don't want to bring in all the real   
   >> dependencies of the code under test, you want to be able to monitor   
   >> and control the interfaces your unit is using.  TO do that, you mock   
   >> them.   
   >   
   > Yes, I agree completely.  The difficulty of Singletons isn't that they   
   > are a singleton per se, but that the SUT has lots of code like this:   
   >   
   > class Single   
   > {   
   > public:   
   > 	static Single *instance();   
   > 	// other public methods but Single is not a pure virtual base   
   > 	void doSomething();   
   > 	void doOtherThing();   
   >   
   > private:   
   > 	Single();   
   > 	~Single();   
   > };   
   >   
   > class SUT   
   > {   
   > public:   
   >       void someMethod();   
   >       // ...   
   > };   
   >   
   > void SUT::someMethod()   
   > {   
   >       Single::instance()->doSomething();   
   >       // ...   
   >       Single::instance()->doOtherThing();   
   >       // ...   
   > }   
   >   
   > ....which means I'm left only with link-time substitution as a way to   
   > mock out the singleton if I leave this code unchanged.   
      
   Which isn't a problem if (like me) link-time substitution or   
   interposition is part of your standard test process.   
      
   > Consider this minor refactoring, however:   
   >   
   > class Single   
   > {   
   > public:   
   > 	virtual ~Single() { }   
   >   
   > 	// pure virtual base defining interface to Single   
   > 	virtual void doSomething() = 0;   
   > 	virtual void doOtherThing() = 0;   
   > };   
   >   
   > class ProductionSingle : public Single   
   > {   
   > public:   
   > 	static Single *instance();   
   >   
   > private:   
   > 	ProductionSingle();   
   > 	virtual ~ProductionSingle();   
   > };   
   >   
   > class SUT   
   > {   
   > public:   
   > 	void someMethod() { someMethod(ProductionSingle::instance()); }   
   > 	void someMethod(Single *single);   
   > };   
   >   
   > void SUT::someMethod(Single *single)   
   > {   
   >       single->doSomething();   
   >       // ...   
   >       single->doOtherThing();   
   >       // ...   
   > }   
   >   
   > Now I can test SUT::someMethod without it being directly coupled to   
   > the production singleton; in fact, there's NOTHING in SUT that   
   > requires it's collaborator to be a singleton, but the choice of making   
   > that collaborator a singleton "leaked" into the implementation of SUT   
   > making it harder to test.   
      
   But haven't you added a new method to SUT just to avoid the coupling?   
      
   > Extracting an interface over Single and   
   > using DI (at the method level) makes testing SUT::someMethod *much*   
   > easier and I don't have to resort to link-time substitution in order   
   > to test the method.  Existing code that calls SUT::someMethod()   
   > doesn't have to change.   
      
   Just about everything in software development is a trade off.  In your   
   case you have added code to SUT to facilitate testing, in mine I would   
   add a description of Single to the file used to generate mocks.  Using   
   abstract classes to support testing is a perfectly valid approach, but   
   you hand code a test derived class where a mock framework will do the   
   code generation for you.   
      
   For example, given your code if you wanted to test SUT::someMethod   
   called doSomething(), you might have to write something like:   
      
   struct TestSingle : Single   
   {   
     bool doSomethingCalled;   
     bool doOtherThingCalled;   
      
     TestSingle() : doSomethingCalled(), doOtherThingCalled() {}   
      
     void doSomething() { doSomethingCalled = true; }   
     void doOtherThing() { doOtherThingCalled = true; }   
   }   
      
   void testSomeMethodCallsDoSomething()   
   {   
     TestSingle single;   
      
     SUT sut;   
      
     sut.someMethod( &single );   
      
     TEST_ASSERT( single.doSomethingCalled );   
   }   
      
   Which isn't too bad for one simple class, but can get messy with more   
   member functions and if there are parameter values to check.   
      
   With the harness I use, I would add an XML description of Single:   
      
       
         
         
         
       
      
   then my test would be   
      
   void testSomeMethodCallsDoSomething()   
   {   
     SUT sut;   
      
     sut.someMethod( &single );   
      
     TEST_ASSERT( Single::doSomething::Called );   
   }   
      
   --   
   Ian Collins   
      
      
         [ See http://www.gotw.ca/resources/clcm.htm for info about ]   
         [ comp.lang.c++.moderated.    First time posters: Do this! ]   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]


(c) 1994,  bbs@darkrealms.ca