如何测试具有共同逻辑的 2 种方法?

How to test 2 methods with common logic?

假设我有 2 个 public 方法:

func didSelect(data: Data) {
    // do something

    self.view.showText(textForData(data))
}

func didDismiss(data: Data) {
    if data.isSomething {
        self.view.showText(textForData(data))
    }

    ...
}

private func textForData(data: Data): String {
    var text: String

    if data.distance == nil {
        text = "..."
    } else if data.distance < 1000 {
        text = "\(data.distance) m"
    } else {
        text = "\(data.distance / 1000) km"
    }

    return text
}

两者都依赖于textForData的格式化逻辑。

textForData 有(通过这个最小化的实现)3 种可能的情况。 如果我确实为我的两个 public 函数测试了所有可能的情况,我将得到 6 种测试方法,其中 3 种也将测试其他 3 种已经测试过的相同逻辑。

测试这个的正确方法是什么?

Ps.: 我可以为 textForData 编写一个单独的测试,在 public 方法的测试中我断言 textForData 被调用,但是似乎破坏了我的 class 的封装,我不想制作 testForData public。 我也不想为我的 textForData 逻辑创建一个单独的 class,因为我最终会为当前的 class 创建太多的依赖关系,而且这个逻辑似乎并不适合除此 class.

之外的任何其他地方

我认为数据的格式化是我自己的责任。所以你应该将它提取到它自己的 class.

这个 class 可以单独进行单元测试。

你的其他使用这个的 class(es) 应该通过使用接口而不是直接使用 class 来解耦。应该注入依赖项(例如在构造函数中)。您可以编写一个默认构造函数来创建默认 class 以简化生产代码中的事情(糟糕的依赖注入)。

然后您可以模拟格式化程序并单独测试其他 class(es),验证是否正确调用了 textForData 方法。

这里有几个选项。

  1. 不测试textForData
  2. 在使用它的 public 方法的每个测试中复制行为
  3. 制作textForDatapublic
  4. textForData
  5. 创建 public class

第 1 点和第 2 点是不可取的。

你似乎很奇怪地反对第 3 点,但这样做有好处。如果您真的很关心这样做,您将能够测试一次这种行为。我不知道 Swift,但在其他语言中,这并不像看起来那么糟糕。一般建议是针对和接口进行编码,而不是实现。所以这个 class 的 public 接口会有 didSelectdidDismiss。您的生产代码将根据此接口表示,这意味着即使 textForData 是 class 上的 public 方法,您也无法直接访问它。

这里的好消息是您的测试可以针对实现编写(事实上,他们必须这样做),因此在这里您可以访问所有三种方法。这样你就可以尽情测试了。

第 4 点与第 3 点类似,但单独存储 class。我会选择这个,因为你可能会争辩说我们已经打破了第 3 点中的单一责任原则。为了隐藏这一点,我会做一个嵌套的 class 开始,因为你声明这段代码只在这个代码中使用例子。同样,您的测试将可以使用与上述相同的想法访问它。

您的代码正朝着拥抱组合的方向发展,因此您应该拥抱诸如小 classes、分解良好的代码等好处。