我有兴趣测试下面的函数 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)) );
}
}
}
Node 是 boost::variant 类型,其中该变体包含 Foo1、Foo2、Bar1 和 Bar2 等类型。
free 函数findFooNode
使用访问者模式来定位 Foo 节点(类型为 Foo1 或 Foo2)
free 函数getCacheIdentifier
还使用访问者模式来定位 Foo 节点的缓存标识符。
_fooContainerCache
是一个依赖注入,并在我的单元测试中被嘲笑。
getName 又是一个免费函数,就像createFoo
.
所有免费函数本身都经过单元测试,并在我的代码中的其他函数中使用。
事情很容易测试到线:
FooContainer& container = _fooContainerCache->get(id);
因为我只需要使用模拟检查预期的 id 是否呈现给 get 函数。
但是,要测试此行之后的代码,我需要检查对从我的模拟中引用返回的 FooContainer 所做的更改。但是,如果 createFoo 将来要更改,我知道它会更改,这导致我必须更改 createFoo 和 navigateFoo 的单元测试。但是,如果我要依赖注入一个 FooFactory,我会避免这个问题,而是这样做:
container.push_back( _fooFactory->create( getData(*fooNodeI) ));
然后我也可以在我的单元测试中模拟这个函数。如果这个接口背后的行为发生了变化,那么它不会导致我必须为navigateFoo重写测试。
然而,当我写 createFoo 时,我从来没有觉得它应该被实现为一个接口是很自然的,所以现在我觉得我添加一个接口只是为了能够编写更好的测试。然后问题出现了,我是否应该为我的任何其他免费功能提供接口?在这方面有什么经验法则吗?