单元测试和私有变量

Rob*_*son 36 bdd unit-testing swift

我正在为公共方法编写BDD单元测试.该方法更改了一个私有属性(private var),所以我想写一个expect()并确保它被正确设置.由于它是私有的,我无法弄清楚如何从单元测试目标访问它.

对于Objective-C,我只需添加一个扩展头.Swift中有类似的技巧吗?作为注释,该属性还有一个带有一些代码的didSet().

Rob*_*ier 47

(请注意,Swift 2添加了@testable可以使内部方法和属性可用于测试的属性.有关更多信息,请参阅下面的@ JeremyP的评论.)

不.在Swift中,私人是私人的.编译器可以使用这个事实进行优化,因此根据您使用该属性的方式,编译器删除它,内联它或做任何其他根据实际代码中的代码提供正确行为的事情是合法的.文件.(优化器今天是否真的那么聪明,它是允许的.)

现在当然如果你宣布你的类是@objc,那么你可以打破这些优化,你可以用ObjC来阅读它.并且有一些奇怪的变通办法可以让你使用Swift来调用任意@objc暴露的方法(比如零超时NSTimer).但是不要这样做.

这是一个经典的测试问题,经典的测试答案是不要这样测试.不要测试内部状态.如果从外面无法告诉事情已经发生,那么就没有什么可以测试的了.重新设计对象,使其在其公共接口上可测试.通常这意味着构图和嘲笑.

可能这个问题最常见的版本是缓存.很难测试某些东西是否真的被缓存,因为唯一的区别可能是它被更快地检索.但它仍然可以测试.将缓存功能移动到另一个对象中,让您的测试对象接受自定义缓存对象.然后,您可以传递一个模拟,记录是否进行了正确的缓存调用(或网络调用,或数据库调用,或内部状态保持的任何内容).

基本上答案是:重新设计,以便更容易测试.

好的,但你真的,真的,真的需要它......怎么做?好吧,有可能不破坏世界.

在要测试的文件中创建一个公开所需事物的函数.不是一种方法.只是一个免费的功能.然后,您可以将该帮助函数放入#if TEST,并TEST在您的测试配置中进行设置.理想情况下,我会让函数实际测试你关心的事情,而不是暴露变量(在这种情况下,也许你可以让函数内部甚至公开).但是无论哪种方式.

  • 不是试图开始一场火焰战,只是为了提出不同的意见 - 我实际上有点不同意这一点.IMO这是一个过于概括的做法,就像说它对测试私有状态或私有方法一样永远不会有用. (12认同)
  • 这是一个非常好的答案,除了第一句不是真的.`@ testable`不会使私有变量可用于测试,只有内部变量可用.因此,Swift 2不会使问题变得微不足道,并且有充分的理由IMO.如果没有办法从外部看到内部状态的影响,谁在乎它是什么? (9认同)
  • 在swift 2.0中不再是这种情况.btw @testable属性用于单元测试,在构建测试时使内部方法公开 (4认同)