通过使它们成为自由函数来对私有方法进行单元测试

Unit test private methods by making them free functions

在 2017 年的 cppcon 视频中,我看到了 Klaus Iglberger 的演讲,题目是“Free Your Functions!”。 在这次演讲中,演讲者谈到了如何切换到免费功能 简化测试私有方法的过程(参见 19:00)。这个想法是你拉 class 中的私有方法(你使它成为一个自由函数)并且它变得可测试。

起初,我觉得这个想法很有趣,但后来 我想得越多,就越不了解这实际上应该如何工作。例如, 假设我有以下(虚拟)class:

class SomeClass
{
public:
    SomeClass();
    ~SomeClass();

    void someTask();

private:

    void someComplexTask();
    void someOtherComplexTask();

};

void SomeClass::someTask()
{
    someComplexTask();
    someOtherComplexTask();
}

// private tasks implementations...

那么someComplexTask()someOtherComplexTask()就是私有方法。这意味着他们 是实现细节,即它们只能在 SomeClass (或朋友)内部调用。它 在我看来,如果你让它们成为自由函数,是的,它们会变得可测试,但它们不再是 私有的,也不仅仅是特定于 SomeClass 的实现细节。事实上,它们可以在代码的任何地方被调用...

所以我的问题是:为什么 Iglberger 先生的观点有效?

我也看了视频。但是,我有一些不同意见。

1- 您的方法是否需要访问字段?如果不是,则不属于class。但如果是这样,他们就需要这些字段。除非您将它们作为函数参数传递,否则自由函数无法访问这些字段。请注意,自由函数不应被视为 public 函数。

2- 并非所有功能都应该是自由功能。但最好避免在不需要时将所有内容都放在 class 中。

3- 私有函数不应该经常被测试。但如果你坚持,你也许可以执行诸如无效的黑客攻击(这并不总是像评论中提到的那样有效):

#define class struct
#define private public
#define protected public

#include "library.h"

#undef class
#undef private
#undef protected

释放你的函数更简洁,但并不更可行。

This is a clear indication that you have a design flaw. If you have a private function that you need to test and you have to bend backwards to make it work then something is wrong. You have missed something. Your design doesn't work.

他的意思不仅仅是让私有函数免费。他不是在说:"get all your private functions and make them free functions"。他说需要测试的功能不应该是实现细节,因为如果您需要测试它,则表明该功能有用。

请关注他对代码的改造:

初始代码:

class X
{
public:
 void doSomething( ... ) {
    ...
    resetValues();
    ...
 }
 ...
private:
 void resetValues() {
     for( int& value : values_ )
        value = 0;
 }
 std::vector<int> values_;
};

他将 resetValuesX 中拉出来,但是 它使它在 std::vector<T> 上运行,而不是在 X 上运行:

void resetValues( std::vector<int>& vec )
{
 for( int& value : vec )
   value = 0;
}

现在 resetValues 是一个可以重复使用和测试的功能。因为它确实与 X 无关,但是通过重置向量的所有值,使其成为自由函数而不是私有 X 方法是一种有效的设计。

我喜欢 Rann Lifshitz 在评论中的表达方式:

I think the better way to go here is to understand that some private functions are, in fact, common utility functions