【发布时间】:2015-04-20 12:12:51
【问题描述】:
我正在为 公共方法 编写 BDD 单元测试。该方法更改了一个私有属性(private var),所以我想编写一个 expect() 并确保它被正确设置。由于它是私有的,我无法弄清楚如何从单元测试目标访问它。
对于 Objective-C,我只需添加一个扩展头。 Swift 中有没有类似的技巧?请注意,该属性还有一个 didSet() 和一些代码。
【问题讨论】:
标签: unit-testing swift bdd
我正在为 公共方法 编写 BDD 单元测试。该方法更改了一个私有属性(private var),所以我想编写一个 expect() 并确保它被正确设置。由于它是私有的,我无法弄清楚如何从单元测试目标访问它。
对于 Objective-C,我只需添加一个扩展头。 Swift 中有没有类似的技巧?请注意,该属性还有一个 didSet() 和一些代码。
【问题讨论】:
标签: unit-testing swift bdd
(请注意,Swift 2 添加了@testable 属性,该属性可以使内部方法和属性可用于测试。有关更多信息,请参阅下面的@JeremyP 的 cmets。)
没有。在 Swift 中,私有就是私有的。编译器可以使用此事实进行优化,因此根据您使用该属性的方式,编译器删除它、内联它或执行任何其他可以根据实际代码给出正确行为的事情是合法的文件。 (无论优化器今天是否真的那么聪明,都是允许的。)
当然,如果你将你的类声明为@objc,那么你可以打破这些优化,你可以用 ObjC 来查看它。还有一些奇怪的解决方法可以让你使用 Swift 调用任意的@objc 暴露方法(比如零超时NSTimer)。但不要那样做。
这是一个经典的测试问题,经典的测试答案是不要这样测试。不要测试内部状态。如果实际上不可能从外部判断出发生了什么事,那么就没有什么可测试的了。重新设计对象,使其可通过其公共接口进行测试。通常这意味着组合和模拟。
这个问题最常见的版本可能是缓存。很难测试是否确实缓存了某些内容,因为唯一的区别可能是检索速度更快。但它仍然可以测试。将缓存功能移动到另一个对象中,并让您的被测对象接受自定义缓存对象。然后,您可以传递一个模拟来记录是否进行了正确的缓存调用(或网络调用、数据库调用或任何内部状态保持)。
基本上答案是:重新设计,以便更容易测试。
好的,但你真的,真的,真的需要它……怎么做?好的,不破坏世界也是可能的。
在要测试的文件中创建一个函数来公开你想要的东西。不是方法。只是一个免费的功能。然后您可以将该辅助函数放入#if TEST,并在您的测试配置中设置TEST。理想情况下,我会让函数实际测试你关心的东西,而不是暴露变量(在这种情况下,也许你可以让函数成为内部甚至公共的)。但无论如何。
【讨论】:
@testable 不使私有变量可用于测试,只有内部变量。因此,Swift 2 并没有使问题变得微不足道,而且 IMO 有充分的理由。如果无法从外部看到内部状态的影响,谁在乎它是什么?
_ 当它们是私有的(就像你在没有私有概念的 python 中所做的那样)。然后你可以测试或不测试它们,因为它是有意义的,仍然有迹象表明它是私有的。