pImpl 习惯用法使测试变得容易得多。看到一组以“不要测试实现”为主题的答案来激励在 OP 之后这么长时间回答,这已经够奇怪了。
通常,基于非 pimpl 的 C++ 有一个具有公共和私有字段的类。公共领域很容易测试,私有领域有点乏味。不过,公共和私有之间的划分很重要,因为它减少了 api 的宽度,并且通常使以后的更改更容易。
使用此成语时,可以使用更好的选择。您可以拥有与单个类完全相同的“公共”接口,但现在只有一个包含某种指针的私有字段,例如
class my_things
{
public:
my_things();
~my_things();
void do_something_important(int);
int also_this();
private:
struct my_things_real;
std::unique_ptr<my_things_real> state;
};
my_things_real 类应该在与外部可见类的析构函数相同的源文件中可见,但在标头中不可见。它不是公共接口的一部分,因此所有字段都可以是公共的。
void my_things::do_something_important(int x) { state->doit(x); } // etc
class my_things_real // I'd probably write 'struct'
{
public:
int value;
void doit(int x) { value = x; }
int getit() { return value; }
};
然后针对真实类编写单元测试。尽可能多地或尽可能少地测试它。我故意将其称为“真实”而不是“实现”,以帮助确保它不会被误认为仅仅是实现细节。
测试这个类非常容易,因为所有字段都是公开的。外部接口非常小,因为它是由另一个类定义的。极薄的翻译层很难出错,但仍然欢迎您通过外部 api 进行测试。从更显着地分离接口和实现来看,这是一个明显的胜利。
在一个模糊的相关说明中,让我觉得很荒谬的是,有这么多原本连贯的人主张跳过对任何无法通过外部 API 轻松访问的东西的单元测试。最低级别的函数几乎不受程序员错误的影响。测试以验证 api 是否可用对于验证实现细节是否正确既重要又正交。