Testing TDD单元测试子方法

Testing TDD单元测试子方法,testing,language-agnostic,tdd,Testing,Language Agnostic,Tdd,我左右为难,不知是否要为重构另一个方法的产品编写方法测试 第一个问题,考虑这个情节。 class Puzzle( val foo: List<Pieces>, val bar: List<Pieces> ) { init { // code to validate foo // code to validate bar } } 在这种情况下,我是否仍然需要测试validatefo和validateBar 第二个

我左右为难,不知是否要为重构另一个方法的产品编写方法测试

第一个问题,考虑这个情节。

class Puzzle(
    val foo: List<Pieces>,
    val bar: List<Pieces>
) {
    init {
       // code to validate foo
       // code to validate bar
    }
}
在这种情况下,我是否仍然需要测试
validatefo
validateBar

第二个问题

class Puzzle(
    val foo: List<Pieces>,
    val bar: List<Pieces>
) {
    ...

    fun getPiece(inPosition: Position) {
        validatePosition()
        // return piece at position
    }

    fun removePiece(inPosition: Position) {
        validatePosition()
        // remove piece at position
    }
}

object PuzzleHelper {

    ...

    fun validatePosition() {
         ...
    }
}
类谜题(
法尔福:名单,
val栏:列表
) {
...
趣味小品(位置:位置){
validatePosition()
//位置处的返回件
}
娱乐移除件(位置:位置){
validatePosition()
//将工件从位置上拆下
}
}
对象帮助器{
...
乐趣验证位置(){
...
}
}
我是否仍然需要为涉及位置验证的
getPiece
removepece
编写测试


我真的很想流利地使用TDD,但不知道如何开始。现在我终于投入进去了,不在乎接下来会发生什么,我想要的只是产品质量。希望很快能听到您的启示。

当我们进入
红色->绿色->重构周期的重构阶段时,我们不应该添加任何新的行为。这意味着所有代码都已经过测试,因此不需要新的测试。您可以通过更改重构代码并观察其测试失败来轻松验证您已经完成了这项工作。如果没有,你就添加了不该添加的内容

在某些情况下,如果提取的代码在其他地方被重用,那么将测试转移到重构代码的测试套件可能是有意义的

至于第二个问题,这取决于您的设计,以及一些代码中没有的内容。例如,如果验证失败,我不知道您想做什么。如果每个方法的验证失败,您必须为这些情况添加不同的测试

我想指出的一点是,将方法放在静态对象(类函数、全局函数,不管您如何调用)中会使测试代码变得更加困难。如果您想在忽略验证时测试类方法(将其存根为始终通过),您将无法做到这一点。

我更喜欢创建一个作为构造函数参数传递给类的协作器。因此,您的类现在获得了一个
validator:validator
,您可以在测试中通过任何想要通过它的内容。存根、真实的东西、模拟等等。

当我们进入
Red->Green->Refactor
周期的重构阶段时,我们不应该添加任何新的行为。这意味着所有代码都已经过测试,因此不需要新的测试。您可以通过更改重构代码并观察其测试失败来轻松验证您已经完成了这项工作。如果没有,你就添加了不该添加的内容

在某些情况下,如果提取的代码在其他地方被重用,那么将测试转移到重构代码的测试套件可能是有意义的

至于第二个问题,这取决于您的设计,以及一些代码中没有的内容。例如,如果验证失败,我不知道您想做什么。如果每个方法的验证失败,您必须为这些情况添加不同的测试

我想指出的一点是,将方法放在静态对象(类函数、全局函数,不管您如何调用)中会使测试代码变得更加困难。如果您想在忽略验证时测试类方法(将其存根为始终通过),您将无法做到这一点。
我更喜欢创建一个作为构造函数参数传递给类的协作器。因此,您的类现在获得了一个
validator:validator
,您可以在测试中通过任何想要通过它的内容。存根、真品、仿制品等

在这种情况下,我还需要测试validatefo和validateBar吗

视情况而定

TDD的一部分要点是,我们应该能够迭代内部设计;又名重构。这是一种魔力,它允许我们从前期设计的一小笔投资开始,并在进行过程中解决其余的问题——事实上我们可以改变事情,测试评估改变而不妨碍

当您的系统所需的行为稳定时,这种方法非常有效

当系统所需的行为不稳定时,当我们有很多行为处于变化中时,当我们知道所需的行为将发生变化但我们不知道会发生什么变化时。。。单一测试跨越许多不稳定的行为往往会使测试变得“脆弱”

在很长一段时间里,这是自动化UI测试的祸根——因为测试UI几乎涵盖了系统每一层的每一个决策,测试一直在维护中,以消除在面对无关紧要的行为变化时出现的一连串误报

在这种情况下,您可能需要开始寻找引入隔板的方法,以防止需求发生变化时过度损坏。我们开始编写测试,以验证测试主题的行为是否与某个更简单的oracle的行为相同,以及测试更简单的oracle是否做了正确的事情

这也是TDD反馈循环的一部分——因为跨越许多不稳定行为的测试是困难的,所以我们重构设计,以支持孤立粒度的测试行为,并以更简单的元素支持更大的组合

在这种情况下,我还需要测试validatefo和validateBar吗

视情况而定

TDD的一部分要点是,我们应该能够迭代内部设计;又名重构。这是一种魔力,它允许我们从前期设计的一小笔投资开始,并在进行过程中解决其余的问题——事实上我们可以改变事情,测试评估改变而不妨碍

当您的系统所需的行为稳定时,这种方法非常有效

当系统所需的行为不稳定时,当我们有很多行为处于fl中时
class Puzzle(
    val foo: List<Pieces>,
    val bar: List<Pieces>
) {
    ...

    fun getPiece(inPosition: Position) {
        validatePosition()
        // return piece at position
    }

    fun removePiece(inPosition: Position) {
        validatePosition()
        // remove piece at position
    }
}

object PuzzleHelper {

    ...

    fun validatePosition() {
         ...
    }
}