Всегда ли Mock объекты в C ++ требуют виртуальных методов или шаблонов? - PullRequest
12 голосов
/ 25 марта 2011

Предположим, у меня есть классы

class Inner {
  public:
    void doSomething();
};

class Outer {
  public:
    Outer(Inner *inner);  // Dependency injection.

    void callInner();
};

Правильное юнит-тестирование говорит, что у меня должны быть тесты на Inner.Тогда у меня должны быть тесты для Outer, в которых используется не реальный Inner, а MockInner, чтобы я выполнял модульные тесты для функциональности, добавленной всего Outer вместо полного стека Outer/Inner.

Для этого Googletest , похоже, предлагает превратить Inner в чистый абстрактный класс (интерфейс), подобный этому:

// Introduced merely for the sake of unit-testing.
struct InnerInterface {
  void doSomething() = 0;
};

// Used in production.
class Inner : public InnerInterface {
  public:
    /* override */ void doSomething();
};

// Used in unit-tests.
class MockInner : public InnerInterface {
  public:
    /* override */ void doSomething();
};

class Outer {
  public:
    Outer(Inner *inner);  // Dependency injection.

    void callInner();
};

Итакв производственном коде я бы использовал Outer(new Inner);во время теста Outer(new MockInner).

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

Альтернативный подход заключается в использовании шаблонов, как показано ниже:

class Inner {
  public:
    void doSomething();
};

class MockInner {
  public:
    void doSomething();
};

template<class I>
class Outer {
  public:
    Outer(I *inner);

    void callInner();
};

// In production, use
Outer<Inner> obj;

// In test, use
Outer<MockInner> test_obj;

Это позволяет избежать нанесения покрытия на котел и ненужной виртуальной отправки;но теперь вся моя кодовая база находится в чертовых заголовочных файлах, что делает невозможным скрытие исходных реализаций (не говоря уже о том, чтобы расстраивать ошибки компиляции шаблонов и длительное время сборки).

Это два метода, виртуальные ишаблоны, единственные способы сделать правильное юнит-тестирование?Существуют ли более эффективные способы для правильного модульного тестирования?

Под надлежащим модульным тестированием я имею в виду каждый модульный тест проверяет только функциональные возможности, представленные этим модулем, но не его зависимости и .

Ответы [ 2 ]

6 голосов
/ 25 марта 2011

Я не думаю, что вы должны издеваться над каждой зависимостью вашего тестируемого класса на практике.Если это сложно создать, использовать или осмыслить, тогда да.Также, если это напрямую зависит от некоторых ненужных внешних ресурсов, таких как БД, сеть или файловая система.

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

Лично я предпочитаю рабочие юнит-тесты и простую, чистую, ремонтопригодную конструкцию, чем придерживаться какой-то идеальной настройки, установленной пуристами юнит-теста.

каждый юнит-тест проверяет только функциональные возможности, представленныеэто устройство, но не его зависимости.

Использование функциональности и тестирование функциональности - это две очень разные вещи.

2 голосов
/ 20 сентября 2011

Я также думаю, что использование экземпляра на Inner напрямую - это нормально.Моя проблема заключается в издевательстве над внешними объектами, которые не являются частью моего кода (предоставляются через статические библиотеки или библиотеки DLL, иногда сторонние).Я склонен переписывать фиктивную DLL или библиотеку с теми же именами классов, а затем по-разному связывать для теста.Изменение заголовочного файла внешней зависимости для добавления «виртуальных» мне кажется неприемлемым.У кого-нибудь есть лучшее решение?

...