如何在私有方法中增加测试覆盖率

时间:2017-01-12 07:28:11

标签: swift tdd

假设我正在编写一个矢量结构

struct Vector3d {
    private var v: Double[3]
    init(_ x: Double, _ y: Double, _ z: Double) { v = [x, y, z] }
    init?(value: Double, at index: Int) {
        guard 0...2 ~= index else { return nil }
        v = [0, 0, 0]
        v[index] = value
    }
    var x: Double { return v[0] }
    var y: Double { return v[1] }
    var z: Double { return v[2] }
    var ortho: Vector3d { // return an arbitrary unit vector which is orthogonal to self
        // Idea: find a vector that is not collinear with self and then cross product of that vector and self will be orthogonal to self
        var largestAbsIndex = 0
        largestAbsIndex = fabs(x) > fabs(y) ? fabs(x) > fabs(z) ? 0 : 2 : fabs(y) > fabs(z) ? 1 : 2
        var indexNextToLargest = largestAbsIndex - 1
        if indexNextToLargest < 0 { indexNextToLargest = 2 }
        return (self ** Vector3d(value: 1, at: indexNextToLargest)!).normalized
    }
    static func **(lhs: Vector3d, rhs: Vector3d) -> Vector3d { /* cross product */}
    var normalized: Vector3d { /* some code */ }
}

在某些时候,我想让组件索引从1开始,即1...3而不是0...2。我修改了第二个初始化程序和相应的测试,全部为绿色

init?(value: Double, at index: Int) {
    guard 1...3 ~= index else { return nil }
    v = [0, 0, 0]
    v[index - 1] = value
}

实际上,ortho属性也取决于初始化程序,也应该更改。但是我在大多数测试中都使用了Vector3d(1, 2, 3),所以当它进入ortho测试时,它仍然有效,因为largestAbsIndex = 2, indexNextToLargest = 1 - 只是幸运。但是,由于Vector3d(1, 4, 3).ortholargestAbsIndex = 1, indexNextToLargest = 0

Vector3d(value: 1, at: 0) == nil会崩溃

我发现了这个错误,并且很明显如何修复它。但是TDD建议我首先编写一个测试来揭示这个bug然后修复生产代码。另一方面,有人说测试用例应该测试行为而不是实现,所以我没有足够的理由来添加Vector3d(1, 4, 3).ortho的新测试,因为这是使这两个测试不同的具体实现细节

那么,我应该添加什么测试来揭示这个错误?

出现这个问题是因为ortho吸气剂正在做一些与它预期行为无关的事情,当然那些不相关的行为没有经过测试。我可以将那些(即largestAbsIndex,indexNextToLargest)提取到其他一些方法,然后分别测试它们。但仍有问题。这些方法当然应该是私有的,而我不能也不应该测试私有方法。有人说我可以将这些方法提取到另一个类,如果不可避免地需要测试私有方法,则测试该类。我可能会提取enumVector3dComponentIndex之类的内容。但是,我仍然无法忍受像公开的那样,因为只有在Vector3d

中使用它才有意义

在任何一种方法中(提取到私有方法或提取到应该是私有的类/结构/枚举),存在如何增加私有代码中的测试覆盖率的问题?

1 个答案:

答案 0 :(得分:0)

在这里要考虑的重要事项是为什么我们试图测试行为而不是实现。我们试图避免的是与当前实现密切相关的测试,我们必须在重构或更改实现时始终不断更改它们。这是我所看到的TDD的好处之一;它迫使你首先考虑该单元的界面,它将如何与周围的事物相互作用。

在您的情况下,您所做的断言是正确的:得到的向量必须是正交的。这与该方法的任何给定实现很好地分离; 然而我们写它,必须是真的。

因此,我认为更改当前测试或添加另一个测试,使用不同的起始矢量来揭示您已识别的边缘情况是完全合理的。如果您尝试将其修复为错误,那就是您要做的事情:编写一个可以重现错误的测试。至关重要的是,如果您以后更改了实施方案,这个新测试仍然会通过,因此它没有指导该指南旨在避免的问题。