How do you mock classes that use RAII in c++

前端 未结 4 791
滥情空心
滥情空心 2020-12-17 22:32

Here\'s my issue, I\'d like to mock a class that creates a thread at initialization and closes it at destruction. There\'s no reason for my mock class to actually create an

相关标签:
4条回答
  • 2020-12-17 23:10

    The pimpl idiom might suit you as well. Create your Thread class, with a concrete implementation that it brings in underneath. If you put in the right #defines and #ifdefs your implementation can change when you enable unit testing, which means that you can switch between a real implementation and a mocked one depending on what you are trying to accomplish.

    0 讨论(0)
  • 2020-12-17 23:19

    First of all, it is not necessarily an unreasonable thing that your classes might be well designed for their use, but poorly designed for testing. Not everything is easy to test.

    Presumably you want to use another function or class which makes use of the class which you want to mock (otherwise the solution is trivial). Lets call the former "User" and the latter "Mocked". Here are some possibilities:

    1. Change User to use an abstract version of Mocked (you get to choose what kind of abstraction to use: inheritance, callback, templates, etc....).
    2. Compile a different version of Mocked for your testing code (for example, #def out the RAII code when you compile your tests).
    3. Have Mocked accept a constructor flag to turn off its behavior. I personally would avoid doing this.
    4. Just suck up the cost of allocating the resource.
    5. Skip the test.

    The last two may be your only recourse if you can not modify User or Mocked. If you can modify User and you believe that designing your code to be testable is important, then you should explore the first option before any of the others. Note that there can be a trade off between making your code generic/flexible and keeping it simple, both of which are admirable qualities.

    0 讨论(0)
  • 2020-12-17 23:23

    You instead make an interface that describes the type, and have both the real class and the mock class inherit from that. So if you had:

    class RAIIClass {
     public:
      RAIIClass(Foo* f);
      ~RAIIClass();
      bool DoOperation();
    
     private:
      ...
    };
    

    You would make an interface like:

    class MockableInterface {
     public:
      MockableInterface(Foo* f);
      virtual ~MockableInterface();
      virtual bool DoOperation() = 0;
    };
    

    And go from there.

    0 讨论(0)
  • 2020-12-17 23:32

    One technique I've used is to use some form of decorator. Your final code has a method which creates its instance on the stack and then calls the same method, but on a member which is a pointer to your base class. When that call returns, your method returns destroying the instance you created.

    At test time, you swap in a mock which doesn't create any threads, but just forwards to the method you want to test.

    class Base{
     protected:
      Base* decorated;
     public:
      virtual void method(void)=0;
    };
    class Final: public Base{
      void method(void) { Thread athread; decorated->method(); } // I expect Final to do something with athread
    };
    class TestBase: public Base{
      void method(void) { decorated->method(); }
    };
    
    0 讨论(0)
提交回复
热议问题