Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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 and close threads. But, to mock a class, I have inherit from it. When I create a new instance of my mock class, the base classes constructor is called, creating the thread. When my mock object is destroyed, the base classes destructor is called, attempting to close the thread.

How does one mock an RAII class without having to deal with the actual resource?

like image 257
Gordon Wilson Avatar asked Oct 12 '08 16:10

Gordon Wilson


People also ask

What is an advantage of using RAII over not using RAII?

Smart pointers use RAII to hide the manipulation of pointers, which are a lower level than business code, so RAII helps respect levels of abstraction in that case too. This is true for resource management in general, including database connection.

What is RAII object?

As the object gets initialized, it acquires the resource it owns. The object is then responsible for releasing the resource in its destructor. The owning object itself is declared on the stack. The principle that objects own resources is also known as "resource acquisition is initialization," or RAII.

When did C++ add RAII?

RAII is associated most prominently with C++ where it originated, but also D, Ada, Vala, and Rust. The technique was developed for exception-safe resource management in C++ during 1984–89, primarily by Bjarne Stroustrup and Andrew Koenig, and the term itself was coined by Stroustrup.

What is an RAII in C++?

Resource Acquisition Is Initialization or RAII, is a C++ programming technique which binds the life cycle of a resource that must be acquired before use (allocated heap memory, thread of execution, open socket, open file, locked mutex, disk space, database connection—anything that exists in limited supply) to the ...


3 Answers

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.

like image 188
hazzen Avatar answered Oct 09 '22 11:10

hazzen


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.

like image 40
ejgottl Avatar answered Oct 09 '22 10:10

ejgottl


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.

like image 22
Mark Kegel Avatar answered Oct 09 '22 11:10

Mark Kegel