Я заинтересован в тестировании функции navigateFoo ниже:
virtual void navigateFoo(const vector<Node>& nodes)
{
// find the foo node in the list of nodes
Nodes::const_iterator fooNodeI = findFooNode(nodes);
// if we have found the foo node
if(fooNodeI!=nodes.end())
{
// the id for retrieving the associated Foo Container from the cache
std::string id = getCacheIdentifier(*fooNodeI);
// the Foo Container associated with the Foo Node
FooContainer& container = _fooContainerCache->get(id);
// the name of the Foo item within the Foo Container
std::string name = getName(*fooNodeI);
// if foo is not found in the associated container, add it
if(findFoo(name, container)==container.end())
{
container.push_back( createFoo(getData(*fooNodeI)) );
}
}
}
Узел имеет тип Boost :: Вариант, где этот вариант содержит типы Foo1, Foo2, Bar1 и Bar2 и т. Д.
Свободная функция findFooNode
использует шаблон посетителя для поиска узла Foo (типа Foo1 или Foo2)
Свободная функция getCacheIdentifier
также использует шаблон посетителя, чтобы найти идентификатор кэша для Foo Node.
_fooContainerCache
является инъекцией зависимостей и проверяется в моем модульном тесте.
getName снова бесплатная функция, как createFoo
,
Все бесплатные функции сами тестируются модулем и используются в других функциях моего кода.
Вещи легко проверяются вплоть до линии:
FooContainer& container = _fooContainerCache->get(id);
так как мне нужно только проверить с макетом, что ожидаемый идентификатор представлен функции get.
Однако, чтобы протестировать код после этой строки, мне нужно проверить изменения, внесенные в FooContainer, возвращенный по ссылке из моего макета. Однако, если createFoo должен был измениться в будущем, что я знаю, это приведет к тому, что мне придется изменить мои модульные тесты для createFoo и navigateFoo. Однако, если бы я вместо этого вставил зависимость FooFactory, я бы избежал этой проблемы, сделав это вместо этого:
container.push_back( _fooFactory->create( getData(*fooNodeI) ));
Затем я могу смоделировать эту функцию и в моем модульном тесте. Если поведение этого интерфейса изменится, то это не приведет к необходимости переписывать тесты для navigateFoo.
Однако когда я писал createFoo, я никогда не чувствовал естественным, что он должен быть реализован как интерфейс, поэтому теперь я чувствую, что добавляю интерфейс просто для того, чтобы иметь возможность писать более качественные тесты. И тогда возникает вопрос, должен ли я иметь интерфейс для любых других моих бесплатных функций? Есть ли какие-то практические правила в этом отношении?
И тогда возникает вопрос, должен ли я иметь интерфейс для любых других моих бесплатных функций?
Это важно для SOLID что у каждого класса должен быть интерфейс. Я лично не следую этому правилу на 100%.
Есть ли какие-то практические правила в этом отношении?
Следуй своей интуиции. Если вы чувствуете, что классу нужен интерфейс для упрощения модульного тестирования, просто добавьте интерфейс. Это упростит вашу жизнь и жизнь сопровождающего.
Тем не менее, следите за YAGNI.
Также, этот вопрос может очистить некоторые сомнения.
Других решений пока нет …