在Swift中单元测试私有变量

3
我很难理解如何在所有字段都是私有的类中实现单元测试,该类正在使用BLE和CoreLocation计算用户的位置 - 这并不重要。我有一个协议,当找到新位置时,我会调用它,符合该协议的所有类将接收一个房间ID和房间名称。因此,这意味着我的类中所有字段都是私有的,因为没有任何外部类应该访问它们,对吧?但是这也意味着我无法测试该类中的任何内容,尽管有一些函数我想测试。我的意思是,我可以将变量从private更改为internal,但这似乎只是为了进行单元测试而做的事情,所以并不正确。我听说过依赖注入,但它似乎需要很多努力。
例如,我有这个函数:

private var beacons: [AppBeacon] = []
private var serverBeacons:[Beacon] = []

private func addBeacons(serverBeacons: [Beacon]){
        for beacon in serverBeacons {
            let beacon = AppBeacon(id: beacon.id, uuid: beacon.uuid, building: beacon.building, name: beacon.name)
            beacons.append(beacon)
       }
   }

例如,我无法测试是否按照我想要的方式填充了信标数组。我的类的公共特性基本上是一个名为startLocating的函数和结果,即房间ID和名称。在黑盒测试中(正确吗?),我应该不关心中间步骤,但是说实话,有这么多的功能,我应该说“不重要”吗?假设我用我选择的rssi值填充了信标,则实际位置算法在node.js服务器上执行,因此我真的不知道客户端应该测试什么?这是经典的MVC,直到我拥有的最后期限,我无法更改它的架构,所以我不知道从哪里入手?只是不测试功能?将字段设置为内部而不是私有?我们会对算法本身进行服务器端的测试,因此已经测试了房间ID是否为预期房间ID。
我在另一篇帖子上读到以下内容: "单元测试的定义是黑盒测试,这意味着您不关心测试单元的内部。您主要是想看看根据单元测试中提供的输入,单元输出了什么。 现在,通过输出,我们可以断定几件事情:" - 方法的结果 - 操作后对象的状态, - 对象具有的依赖项的交互
"在所有情况下,我们只关心公共接口,因为那是与世界其他部分通信的接口。私有内容不需要进行单元测试,因为任何私有项目都是由公共项目间接使用的。关键是编写足够的测试来运行公共成员,以便完全覆盖私有成员。 此外,要记住的一件重要事情是,单元测试应验证单元规格,而不是实现。验证实现细节会在单元测试代码和测试代码之间产生紧密耦合,这具有很大的缺点:如果测试实现细节发生更改,则可能需要更改单元测试,这降低了为该代码单元编写单元测试的好处。"
从这个描述中,我理解为我应该不进行单元测试?

2
通常情况下,您有使用/读取属性的函数,因此您会对它们进行单元测试。例如,要测试 beacons 属性,您可以测试一个使用该属性的函数,并在调用要测试的函数之前通过调用 addBeacons 来设置它。 - Joakim Danielson
是的,但是假设我调用了addBeacons方法,我不能只写:addBeacons(); XCTAssertEqual(beacons.first.id, "id")因为beacons是私有的,所以我根本无法访问该属性。 - Christian Hjelmslund
这完全不是我说的,你必须编写一个单元测试来测试一个从“beacons”属性读取以测试它是否已被“addBeacons”正确设置的函数。通过测试类的公共API来测试类,并且该公共API由所有公共函数和属性组成。 - Joakim Danielson
抱歉,也许我完全脑残了,但我不明白你所说的从beacons读取数据时,因为beacons是私有的?甚至addBeacons也是私有的?在我的测试套件中,我甚至不能调用addBeacons。我能调用的是startLocating(),它确实调用另一个函数,然后调用addBeacons(),但该类中所有函数的结果本质上都是房间名称和房间ID,并没有验证addBeacons()是否正常工作。我不知道我是不是太蠢了,但你能否再详细解释一下呢? - Christian Hjelmslund
抱歉,我漏掉了 addBeacons 也是私有的,但是我关于编写公共API测试的说法仍然成立。 - Joakim Danielson
有一段非常好的WWDC视频,讲解单元测试以及如何正确地构建代码,以便进行单元测试。我强烈建议你观看它。 - matt
2个回答

1
如果您有一个可以帮助编写单元测试的私有变量,请将其更改为private(set) var,以便可以读取(但不能更改)。
透露内部可能会让您感到不安。如果是这样,可能还有另一种类型等待从视图控制器中提取出来。

0
首先,你引用的“单元测试”定义相当不寻常:我所知道的所有文献中的定义都认为单元测试是一种白盒测试方法。更准确地说,单元测试本身既不是黑盒也不是白盒——区别在于用于设计测试用例的方法。但是,没有理由不使用白盒测试设计技术进行单元测试。事实上,有些白盒测试设计技术只有在应用于单元测试时才有意义。例如,当你研究单元测试时,你会遇到很多关于不同代码覆盖率标准的讨论,比如语句覆盖率、分支覆盖率、条件覆盖率、MC/DC等,对于所有这些标准,在测试用例设计期间了解代码的实现细节并考虑这些实现细节是至关重要的。
但是,即使考虑到这一点,对于你的特定情况也没有太大改变:你代码中的变量仍然是私有的 :-) 然而,你以一种过于限制的方式来解决测试问题:你试图将函数addBeacons本身进行测试,甚至与该组件中的其他函数完全隔离开来。

为了测试addBeacons,你的测试用例不仅应该包含对addBeacons的调用,还应该包含一些其他函数的调用,其结果将显示出addBeacons的调用是否成功。例如,如果您还有一个按名称或位置查找信标的函数,您将首先调用addBeacon,然后调用其中一个函数来检查是否已成功添加信标。这两个调用将成为同一个测试用例的一部分。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接