你如何模拟在c ++中使用RAII的类

时间:2008-10-12 16:47:18

标签: c++ unit-testing mocking

这是我的问题,我想模拟一个在初始化时创建线程并在销毁时关闭它的类。我的模拟类没有理由实际创建和关闭线程。但是,为了模拟一个类,我继承了它。当我创建mock类的新实例时,会调用基类构造函数,从而创建线程。当我的模拟对象被销毁时,将调用基类析构函数,尝试关闭该线程。

如何在不必处理实际资源的情况下模拟RAII类?

4 个答案:

答案 0 :(得分:12)

您改为创建一个描述该类型的接口,并让真实类和模拟类继承该类型。所以如果你有:

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

 private:
  ...
};

您可以创建一个界面:

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

从那里开始。

答案 1 :(得分:6)

首先,您的课程可能非常适合其使用,但设计不合理,不一定是不合理的。并非一切都很容易测试。

大概你想要使用另一个函数或类来使用你想要模拟的类(否则解决方案是微不足道的)。让我们称之为“用户”,后者称为“Mocked”。以下是一些可能性:

  1. 更改用户使用Mocked的抽象版本(您可以选择使用哪种抽象:继承,回调,模板等....)。
  2. 为您的测试代码编译不同版本的Mocked(例如,在编译测试时#def out RAII代码)。
  3. 让Mocked接受构造函数标志以关闭其行为。我个人会避免这样做。
  4. 只需支付分配资源的费用。
  5. 跳过测试。
  6. 如果您无法修改User或Mocked,则最后两个可能是您唯一的追索权。如果您可以修改用户,并且您认为将代码设计为可测试很重要,那么您应该先探索其他任何一个选项。请注意,在使代码具有通用性/灵活性和保持简单性之间存在折衷,这两者都是令人钦佩的品质。

答案 2 :(得分:1)

pimpl成语也可能适合你。创建你的Thread类,它带有一个带来的具体实现。如果你输入正确的#defines和#ifdefs,你的实现可以在启用单元测试时改变,这意味着你可以根据你想要完成的内容在真实实现和模拟实现之间切换。

答案 3 :(得分:0)

我使用的一种技术是使用某种形式的装饰器。你的最终代码有一个方法,它在堆栈上创建它的实例,然后调用相同的方法,但是在一个指向基类的指针的成员上。当该调用返回时,您的方法将返回销毁您创建的实例。

在测试时,你交换了一个不会创建任何线程的模拟,只是转发到你想要测试的方法。

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(); }
};