Как вы издеваетесь над классами, которые используют RAII в C ++ - PullRequest
9 голосов
/ 12 октября 2008

Вот моя проблема, я хотел бы посмеяться над классом, который создает поток при инициализации и закрывает его при уничтожении. У моего фиктивного класса нет причин создавать и закрывать темы. Но, чтобы издеваться над классом, я унаследовал от него. Когда я создаю новый экземпляр моего ложного класса, вызывается конструктор базовых классов, создающий поток. Когда мой фиктивный объект уничтожается, вызывается деструктор базовых классов, пытающийся закрыть поток.

Как можно издеваться над классом RAII, не имея дело с реальным ресурсом?

Ответы [ 4 ]

12 голосов
/ 12 октября 2008

Вместо этого вы создаете интерфейс, описывающий тип, и наследует от него как реальный класс, так и класс mock. Так что, если у вас было:

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

 private:
  ...
};

Вы бы сделали интерфейс вроде:

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

И иди оттуда.

6 голосов
/ 12 октября 2008

Прежде всего, необязательно, что ваши классы могут быть хорошо спроектированы для их использования, но плохо спроектированы для тестирования. Не все легко проверить.

Предположительно, вы хотите использовать другую функцию или класс, который использует класс, который вы хотите смоделировать (иначе решение тривиально). Давайте назовем первого «пользователя», а второго «издеваться». Вот несколько возможностей:

  1. Измените Пользователя на использование абстрактной версии Mocked (вы можете выбрать, какую абстракцию использовать: наследование, обратный вызов, шаблоны и т. Д ....).
  2. Скомпилируйте другую версию Mocked для вашего кода тестирования (например, #def из кода RAII при компиляции ваших тестов).
  3. Пусть Mocked принимает флаг конструктора, чтобы отключить его поведение. Лично я бы избегал этого.
  4. Просто возьмите на себя расходы по выделению ресурса.
  5. Пропустить тест.

Последние два могут быть вашим единственным выходом, если вы не можете изменить User или Mocked. Если вы можете изменить User и считаете, что разработка кода для тестируемости важна, тогда вам следует изучить первый вариант, прежде чем какой-либо другой. Обратите внимание, что может быть компромисс между тем, чтобы сделать ваш код универсальным / гибким и сделать его простым, и то, и другое - замечательные качества.

1 голос
/ 13 октября 2008

Идиома прыщей может подойти и вам. Создайте свой класс Thread с конкретной реализацией, которую он вносит внизу. Если вы введете правильные значения #defines и #ifdefs, ваша реализация может измениться, когда вы включите модульное тестирование, что означает, что вы можете переключаться между реальной реализацией и проверенной.

0 голосов
/ 13 октября 2008

Одна из техник, которые я использовал, - это использование какой-то формы декоратора. В вашем конечном коде есть метод, который создает свой экземпляр в стеке и затем вызывает тот же метод, но для члена, который является указателем на ваш базовый класс. Когда этот вызов возвращается, ваш метод возвращает уничтожение созданного вами экземпляра.

Во время тестирования вы меняете макет, который не создает никаких потоков, а просто переходит к методу, который вы хотите проверить.

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