【问题标题】:Using a friend class vs. adding accessors for unit testing in C++?在 C++ 中使用朋友类与添加访问器进行单元测试?
【发布时间】:2009-07-19 04:35:17
【问题描述】:

添加返回对象内部状态以进行单元测试的函数是否比让测试类成为朋友更好? - 尤其是当除了单元测试的情况之外没有使用功能时。

【问题讨论】:

标签: c++ unit-testing friend-class


【解决方案1】:

单元测试应该 95% 的时间只测试一个类的公开表面。如果您在幕后测试某些东西,那就是测试实现细节,这本质上是脆弱的,因为您应该能够轻松更改实现并且仍然让测试工作。它不仅脆弱,而且您还可能会尝试测试在计划使用场景中实际上不可能的东西,这是浪费时间。

如果你要添加的访问器的目的只是为了测试函数是否达到了预期的效果,那么你的类设计可能会违反另一个原则,那就是类状态机的类应该始终明确什么状态它在,如果这会影响人们与班级互动时发生的事情。在这种情况下,提供那些只读访问器是正确的。如果它不影响类的行为,请参考我之前关于实现细节的部分。

正如你所说的那样,用未使用的东西弄乱一个类的公共表面也是不可取的,因为它有其自身的原因。

如果我不得不在你的情况下在访问者和加好友之间做出选择,我会选择加好友,因为拥有你的测试并且可以在紧要关头改变它。你可能不拥有那些找到使用额外访问器的方法的小丑的代码,然后你就会被卡住。

【讨论】:

    【解决方案2】:

    我不同意接受的答案,而是建议使用朋友类。

    您正在测试的部分状态可能特定于您的类的实现;您正在测试其他代码通常不知道或不关心且不应依赖的细节。公共访问器函数使这些实现细节成为类接口的一部分。如果您正在测试的内部状态不是预期接口的一部分,则它不应该通过公共函数可见。从纯粹主义者的角度来看,您被困在两个错误答案之间,因为朋友类在技术上也是公共接口的一部分。在我看来,问题变成了,哪个选项不太可能导致未来的编码选择不佳?使用一组依赖于实现的公共访问器函数会无意中鼓励类的依赖于实现的概念模型,从而导致依赖于实现的类的使用。适当命名和记录的单个朋友类不太可能被滥用。

    虽然我强烈同意优先使用访问器函数而不是直接访问成员变量的建议,但我不同意这种最佳实践适用于依赖于实现的内部状态的单元测试。一个合理的中间立场是使用 private 访问器函数来处理您的单元测试将关心的状态,并有足够的纪律来在您的单元测试中使用访问器函数。只是我的看法。

    【讨论】:

      【解决方案3】:

      使用友元类进行单元测试是完全合法的,并且允许您维护封装。您不应该仅仅为了使类更可测试而修改您的类公共接口。这样想吧。如果您购买了第三方 FTP 库并且您正在尝试使用它,并且它的公共界面充满了一堆您甚至不需要仅仅因为单元测试而知道的方法!即使修改受保护的接口以补偿单元测试也是不好的。如果我从某个类继承,我不想担心哪些方法对我有用,哪些方法只是因为单元测试才存在!!!使用friends 类进行单元测试可以帮助您维护一个简单、易用的类接口;它有助于保持封装和抽象!!!

      我听说过使用友元类进行单元测试不好的论点,因为被测类不应该与其测试类“紧密耦合”,而且它不应该“知道”任何关于它的测试类的信息。我不买这个。这是添加到类顶部的一行:

      朋友班 MyClassTest;

      现在你可以用任何你想要的方式测试你的课程了!

      现在我同意你不应该使用朋友类,除非有必要。如果您可以测试需要测试的内容而无需成为朋友,那么一定要这样做。但是,如果生活变得困难并且使用朋友课程让生活再次变得轻松,那就使用它吧!

      【讨论】:

        【解决方案4】:

        我建议使用访问器而不是允许通过公共成员或朋友类进行访问。

        我不认为使用朋友班实际上会给你带来任何好处,而且它有可能让你的生活变得更糟。如果您的代码将长期存在,那么它很有可能会以您未预料到的方式使用。访问功能现在可能只用于测试,但谁知道将来会发生什么?使用访问器而不是提供对变量的直接访问为您提供了更多的灵活性,并且成本非常低。

        另一个论点是使用访问器而不是公共成员是一个好习惯。养成良好的习惯是程序员的一项重要技能。

        【讨论】:

        • 为了确保我理解你,变量 double time 的访问器;我创建会创建:double time() const;
        • 是的,有两个原因:1,它告诉其他程序员您希望他们能够对数据做什么,以及 2:编译器通常可以优化 const 函数,而没有 const标识符。
        • 哦,是的,注意有一个变量 time 和一个函数 time() 不会编译;大多数人都有一个带有下划线的实际变量,比如 time_,以及像 time() { return time_; 这样的访问器。 }
        【解决方案5】:

        让内部状态“受保护”怎么样? 然后使用派生类进行单元测试。

        【讨论】:

        • protected 的封装保护比朋友最差。从您的类继承的任何人都将具有合法访问权限。而且因为你把它们保护起来,你说这确实是支持的。至少,具有特定名称friend class MyObjectOnlyForTestingPurpose 的朋友类将限制对具有 ONE 特定名称的 ONE 类的访问。 . .总而言之,这就像把你公寓的钥匙给一个朋友,而不是把钥匙给你家里的每个人(包括你女儿丈夫的同父异母的兄弟)
        【解决方案6】:

        我认为,通过向其用户提供访问器(如果这样做是有意义的)和提高可测试性来保证类的未来发展需要有所区别。我也不喜欢仅仅为了测试而与类交朋友,因为这会在我不想拥有它的地方引入紧密耦合。

        如果访问器的唯一用途是为测试用例提供一种检查类内部状态的方法,那么公开它们通常是没有意义的。它还可以限制您以后可能希望更改但发现由于其他人正在使用所述访问器而无法更改的实现细节。

        我首选的解决方案是提供受保护的 访问器函数,以便向类的用户清楚地传达这些不是公共接口的一部分。然后,您的测试将创建一个原始派生类的最小派生类,其中包含父函数的调用存根,但也会公开访问器,以便您可以在测试用例中使用它们。

        【讨论】:

        • 不会将派生类视为该类的客户/用户,就像公共接口的客户/用户一样?除非唯一的派生类是单元测试存根,否则您将遇到与公开访问器相同的问题 - 派生类的编写者无法判断哪些受保护的函数可以调用,哪些应该仅用于测试目的。如果更改与测试相关的函数,可能会破坏不知道不使用访问器的派生类。
        猜你喜欢
        • 2012-11-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-28
        • 2011-12-11
        • 2023-03-26
        • 2011-02-16
        • 1970-01-01
        相关资源
        最近更新 更多