Предположим, у меня есть классы
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;
Это позволяет избежать нанесения покрытия на котел и ненужной виртуальной отправки;но теперь вся моя кодовая база находится в чертовых заголовочных файлах, что делает невозможным скрытие исходных реализаций (не говоря уже о том, чтобы расстраивать ошибки компиляции шаблонов и длительное время сборки).
Это два метода, виртуальные ишаблоны, единственные способы сделать правильное юнит-тестирование?Существуют ли более эффективные способы для правильного модульного тестирования?
Под надлежащим модульным тестированием я имею в виду каждый модульный тест проверяет только функциональные возможности, представленные этим модулем, но не его зависимости и .