Loading ...
Sorry, an error occurred while loading the content.

118821Re: [XP] Re: TDD and C++

Expand Messages
  • David Carlton
    Apr 8, 2006
    • 0 Attachment
      On Sat, 08 Apr 2006 16:50:04 +0200, Philippe AMELINE <philippe.ameline@...> said:
      > David Carlton a écrit :

      [ advantages for using constructor/destructor in place of
      setUp/tearDown deleted ]

      [ other miscellaneous comments deleted ]

      > This point really seems of interest, but the way you describe it is
      > too technical to be plainly understandable for me... and probably
      > for some others ;-)

      > Can you illustrate what you say by some simple examples... or point
      > out some references?

      I'm happy to talk about this at length - just tell me what you want me
      to talk about. Do you want me to talk about the advantages of doing
      things in the way I recommend, how to implement it, or something else?

      If the implementation is the question, here's a sketch of how I
      refactored from the old way of doing things to the new way of doing
      things.

      WARNING: I am typing this code off of the top of my head; I would be
      shocked if I didn't make a typo somewhere. I hope the ideas are
      there, though.

      In the old version, I had classes like this:

      class Test {
      public:
      virtual void setUp() {}

      virtual void run() = 0;

      virtual void tearDown() = 0;
      };

      class Suite : public Test {
      public:
      void run(); // sets up, runs, tears down all tests in list

      void addTest(Test *test); // adds a test to the list

      private:
      std::list<Test *> tests_;
      };


      This might be used as follows:

      class StackBase : public Test {
      public:
      void setUp() { stack_ = new Stack(); }

      void tearDown() { delete stack_; }

      protected:
      Stack &stack() { return *stack_; }

      private:
      Stack *stack_;
      };

      class PushPop : public StackBase {
      public:
      void run() {
      ASSERT(stack().empty());

      stack().push(1);

      ASSERT(!stack().empy());
      ASSERT_EQUAL(1, stack().pop());

      ASSERT(stack().empty());
      }
      };

      // Other stack tests go here.

      class StackSuite : public Suite {
      public:
      void setUp() {
      newTest(new PushPop())
      // Add other stack tests here.
      }
      };


      Which is fine (and we did things this way for maybe a couple of
      years), but it has its limitations. For example, if the individual
      tests want to add more setup/teardown functionality, it's easy to
      forget to call the base class's setup/teardown, or to call it in the
      wrong place.

      So we added the following:

      template<typename T>
      class TestHolder : public Test {
      public
      void setUp() { test_ = new T(); }

      void run() { test_->run(); }

      void tearDown() { delete test_; }
      };

      And we added another addTest() method to Suite:

      class Suite {
      public:
      // Rest as above.

      template<typename T>
      void addTest() {
      addTest(new TestHolder<T>);
      }
      };

      Then our earlier StackBase example turns into this:

      class StackBase {
      protected:
      Stack &stack() { return stack_; }

      private:
      Stack stack_;
      };

      No need for explicit setUp/tearDown at all: the compiler generated
      default constructor/destructor do our job for us. (It's also not
      unusual for setUp to be replaced by a short constructor that we have
      to write, and for tearDown to vanish.)

      And StackSuite just does

      addTest<PushPop>();

      instead of

      addTest(new PushPop());

      Which is no harder (or easier) than the other version.


      Is this useful? Please tell me areas where you'd like elaboration
      (either here or in my earlier e-mail), and I'll be happy to say more.

      David Carlton
      carlton@...
    • Show all 27 messages in this topic