匿名名称空间使代码不可测试


12

这是典型的C ++代码:

foo.hpp

#pragma once

class Foo {
public:
  void f();
  void g();
  ...
};

foo.cpp

#include "foo.hpp"

namespace {
    const int kUpperX = 111;
    const int kAlternativeX = 222;

    bool match(int x) {
      return x < kUpperX || x == kAlternativeX;
    }
} // namespace

void Foo::f() {
  ...
  if (match(x)) return;
  ...

它看起来像一个不错的惯用C ++代码-一个类,一个辅助函数match,由的方法使用Foo,该辅助函数的一些常量。

然后我要编写测试。
为编写一个单独的单元测试将是完全合乎逻辑的match,因为这是不平凡的。
但是它驻留在匿名名称空间中。
当然,我可以编写一个调用的测试Foo::f()。但是,如果测试Foo过于繁琐,将不是一个很好的测试,这种测试不会将受测者与其他无关因素隔离开。

因此,我必须将match所有其他内容移出匿名名称空间。

问题:将函数和常量放入匿名名称空间有什么意义(如果它使它们在测试中不可用)?


3
@BЈовић重新读取代码-匿名名称空间在中foo.cpp,而不是标题中!OP似乎非常了解您不应在标题中放置匿名命名空间。
阿蒙

在未命名的命名空间中对C ++代码进行单元测试中的一些想法……但是“要点”是封装是好的。毕竟,私有成员函数是同样的问题:它们不能进行非侵入式测试,但是您不想放弃隐藏信息以进行单元测试(例如stackoverflow.com/a/3676680/3235496)。
manlio

1
从概念上讲,您的案例与测试(或不测试)私有方法的案例没有太大区别。因此,当您在Programmers上搜索“私有单元测试”时,您将获得许多答案,可以直接将其应用于您的案例。
布朗

@DocBrown我不是问如何在不久的将来测试功能。命名空间。我问“为什么要将代码放入匿名命名空间?” (请参阅问题末尾的文字)。责备Ixrec,将标题更改为其他名称。
Abyx

2
@Abyx:在我上面提到的其他答案中,您会在这里找到许多专家的广泛共识,即测试私有方法是一个非常糟糕的主意,并且friend不建议为此目的滥用关键字。如果对方法的限制导致无法再直接测试它的情况,则意味着私有方法无用。
布朗

Answers:


7

如果要对私有实现细节进行单元测试,则对未命名名称空间的处理与对私有(或受保护)类成员的处理相同:

参加聚会。

对于类,您滥用friend;对于未命名的名称空间,您滥用#include-mechanism,这甚至不强迫您更改代码。
现在,您的测试代码(或最好只公开所有内容的代码)在同一TU中,就没有问题了。

请注意:如果您测试实现细节,那么如果更改细节,测试将失败。确实要确保仅测试那些可能会泄漏的实现细节,或者接受您的测试异常短暂。


6

您的示例中的函数看起来相当复杂,出于单元测试的目的,最好将其移至标头。

将函数和常量放入匿名名称空间的意义何在?

使他们与世界其他地方隔离。您不仅可以在匿名名称空间中放置函数和常量,还可以用于类型。

但是,如果这使您的单元测试非常复杂,那么您做错了。在这种情况下,该功能不属于该功能。然后是时候进行一些重构以简化测试了。

因此,在匿名名称空间中,只应使用非常简单的函数,有时在该转换单元中使用常量和类型(包括typedef)。


5

编写一个单独的单元测试进行匹配是完全合乎逻辑的,因为这是不平凡的。

您显示的代码match是一个非常简单的1-liner,没有任何棘手的边缘情况,还是像一个简化的示例?无论如何,我认为它是简化的...

问题:将函数和常量放入匿名名称空间有什么意义(如果它使它们在测试中不可用)?

这个问题是让我跳入这里的原因,因为Deduplicator已经显示出一种完美的闯入方式并可以通过#include骗术进行访问。但是,这里的措辞听起来像是测试所有事物的每个内部实现细节都是某种通用的最终目标,而远非如此。

甚至单元测试的目标并不总是要测试每个微小的内部微单元功能。同样的问题适用于C.你的静态文件范围功能,甚至可以使这个问题更难回答问为什么开发人员使用pimplsC ++中这需要双方 friendship#include弄虚作假白盒,交易的实施细则,以提高编译时间方便的可测性,例如

从一种务实的角度来看,它听起来可能很粗糙,但match在某些导致它崩溃的极端情况下可能无法正确实现。然而,如果唯一的外部类,Foo即具有访问match不可能使用它的方式,遇到那些边缘的情况下,那么这是相当无关的正确性Foomatch有永远不会,除非遇到这些边缘情况Foo的变化,在这一点的测试Foo将失败,我们将立即知道。

渴望测试每个内部实现细节(例如,可能是关键任务软件)的人更加痴迷,可能想闯入聚会,但很多人不一定认为这是最好的主意,因为它会创造出可以想象到的大多数脆性测试。YMMV。但是我只是想解决这个问题的措辞,这听起来像是这种超级细粒度的内部细节级别的可测试性应该是最终目标,即使是最严格的单元测试心态也可能在这里有所放松。并避免对每个班级的内部进行X光检查。

那么,为什么人们在C ++中的匿名命名空间中定义函数,或者在C中将其定义为具有内部链接的文件范围静态函数,而这些函数却对外界隐藏?基本上就是这样:将它们与外界隔离。从减少编译时间到降低复杂度(无法在其他地方访问不会在其他地方引起问题)等方面,这具有许多影响。私有/内部实现细节的可测试性并不是人们完成工作时想到的第一件事,例如,减少构建时间并向外界隐藏不必要的复杂性。


我希望我可以投票十次。作为与关键任务,高保证软件相关的切线,您将更加关注细节的正确性。这种C ++惯用的样式不适合这种情况。我不会使用该语言的功能,例如匿名名称空间或类似代理的模式。我将使用[支持的]用户空间标头和[不受支持的]开发人员空间标头粗略地控制边界。
戴维
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.