您是将私有内容内部/公开进行测试,还是使用诸如PrivateObject之类的黑客工具?


26

我是代码测试的初学者,assert以前是妓女。在单元测试中让我担心的一件事是,通常需要您创建public(或至少internalprivate原本应该不是的字段,取消readonly它们,改为创建private方法protected virtual等。

我最近发现,可以通过使用诸如PrivateObject类之类的东西通过反射访问对象中的任何内容来避免这种情况。但是,这会使您的测试难以维护(事情将在执行而不是编译时失败,它会被简单的重命名破坏,更难调试...)。你对此的看法如何 ?关于访问限制的单元测试的最佳实践是什么?

编辑:例如,假设您有一个类,该类在磁盘上的文件中具有缓存,而在测试中您想写入内存。


2
蟒蛇人只有公开的。他们很高兴。为什么要担心?为什么不公开所有内容?
S.Lott

12
因为这与大多数顶级语言的最佳实践相去甚远。真正的答案是使用良好的接口,这样您就可以使用模拟对象对其进行测试。
钻机

2
@Rig:Python不是顶级语言?有趣。
S.Lott

7
“在大多数顶级语言中,这与最佳实践相去甚远”在逻辑上并不暗示(甚至暗示)“ Python不是顶级语言”。
Mike Nakis 2011年

1
确切地说,它是一种顶级语言,但是大多数顶级语言都不是Python,并且认可设置适当的范围。我支持我的声明。有些模式旨在使高度可测试的软件,同时确保变量保持范围。甚至Python程序员也倾向于使用我所看到的前缀来模拟作用域。
钻机

Answers:


36

您永远都不必

制作public(或至少internalprivate原本应该取消的字段,而是使用readonlymake private方法protected virtual

尤其是对字段进行非只读操作会使不可变的对象可变,这将是一场灾难。

测试应将对象视为黑盒Wikipedia),这意味着它们仅应与对象的公共接口有关,而与它们的实现细节无关。

如果无法使用其公共接口对对象进行充分的测试,则需要找到一种方法来为其接口提供形式化和有用的扩展,以方便测试。例如,即使手头的应用程序不必要,只有一对Register / Deregister方法的注册系统也可能会受益于IsRegistered方法。仍然,它是对该接口的正式且有用的扩展,并且顺便说一句,它将适应测试。

重要的是,更改对象的实现不应要求您更改单元测试。从理论上讲,您应该能够编写一次单元测试,然后要求多个程序员对要测试的对象的多个完全不同的实现进行编码,以与单元测试一起工作。


2
究竟!如果您不能在没有反思或黑客攻击的情况下对其进行单元测试,则可能是您的API或设计有误。
猎鹰

你是说,制作private方法protected virtual与在测试中嘲笑帮助被认为是不好的做法?
罗布拉

1
@Robula在我的书中,是的。我什至没有将测试代码与生产代码放在同一程序包中,因为我无法访问非公共成员。我也不使用模拟。当然,如果您必须使用模拟,那么您将必须做任何事情来促进模拟,因此在许多情况下,受保护的虚拟可能是一种实际的折衷方案。但是理想情况下,设计不需要模拟,仅需要其他实现,理想情况下,测试是黑盒测试,而不是白盒测试,因此无需集成即可对事物进行集成测试。
Mike Nakis

13

在C#中,您可以使用InternalsVisibleToAttribute允许您的测试程序集查看internal正在测试的程序集中的类。听起来您已经知道这一点。

在大多数情况下,我只对测试程序集的公共API感兴趣。在要对某个单元进行白盒测试的情况下,我将代码移至一个internal类,并使其成为public该类的方法。然后,我可以为该类编写单元测试代码。

这非常适合依赖项注入和策略模式。您可以将方法抽象到一个接口中,将该接口注入到父对象的构造函数中,然后测试父对象是否适当地委托。然后,您可以测试子对象的行为是否正确。这使一切都更加模块化。


8

根据我的经验,最好不要专门针对测试公开您的课程内部。即使这看起来会使测试更容易编写。该测试是您班级的第一个客户,因此,如果很难通过其公共接口来测试您的班级,那么可能也很难在其他地方使用您的班级。

该类通过其公共API进行测试的难易程度是有关该类使用的难易程度的反馈。将测试部分地看作是原型使用类的一种方法。

尝试考虑一下为什么您的测试需要感知有关公共API无法提供的有关类的某些信息。也许您的课程做得太多,需要分解为一组合作的课程?然后,您可以模拟一些协作类以感知被测类在做什么。

尝试应用依赖关系反转主体,并在类之间引入接口,您可以在测试中模拟这些接口。您可以通过使用控制反转为测试提供协作者。

还可以考虑应用“告诉不要问”主体,以一种健壮的,松散耦合的方式来帮助构造您的类接口,在这种情况下,正确的信息将被公开,而其余信息将被隐藏。


6

就我个人而言,我宁愿让我正在测试的代码尽量保持纯净,如果测试代码需要破解(以及C ++讨厌的指针等),我很乐意这样做。



1
我同意,这是这样做的方法。将难看的代码保留在测试代码中,它不会伤害任何东西。
艾伦·德利蒙

@AlanDelimon可能不会伤害后续维护程序员的思想吗?
flamingpenguin 2011年

1
@flamingpenguin难看的代码可能会伤害任何地方的程序员;但是丑陋的测试代码所带来的危害可能较小,因为它只会影响修改类的人,而不会影响仅使用它的人。
Dan Neely

2
@flamingpenguin可能,但是,如果您必须将难看的代码放在某个地方,那么它也可能处于单元测试中,因此不太可能需要修改并成为应用程序的一部分。
艾伦·德利蒙

5

代码的可见性与单元测试无关!

您将自己的方法设为私有,而不是为了将其隐藏在测试之外,并且不将其公开以进行测试,对吗?这是您在这里必不可少的实现,而不是测试-这些服务器是您代码的证明,但它们不是您的代码

有很多方法可以访问隐藏的(私有或内部)方法和属性。许多测试框架和IDE(例如Visual Studio)通过生成访问所需的一切来支持您,您只需要编写测试用例即可。那么,为什么要担心呢?最好保持代码整洁。

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.