0

我有兴趣测试下面的函数 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 时,我从来没有觉得它应该被实现为一个接口是很自然的,所以现在我觉得我添加一个接口只是为了能够编写更好的测试。然后问题出现了,我是否应该为我的任何其他免费功能提供接口?在这方面有什么经验法则吗?

4

1 回答 1

1

然后问题出现了,我是否应该为我的任何其他免费功能提供接口?

对于SOLID,每个类都应该有一个接口是必不可少的。我个人不会 100% 遵守这条规则。

在这方面有什么经验法则吗?

跟随你的直觉。如果你觉得你需要一个类需要一个接口来简化单元测试,那么只需添加一个接口。它将简化您和维护者的生活。

但是,请注意YAGNI


此外,这个问题可能会消除一些疑问。

于 2013-04-29T13:56:40.327 回答