当TDD测试显示需要进行测试的新功能时该怎么办?


13

当您编写测试并达到需要通过测试并意识到需要附加功能的时候,该怎么做?该新功能也需要进行测试,但是TDD周期表示“使测试失败,使其通过然后进行重构”。如果我要通过测试,那我就不应该继续进行另一个失败的测试来测试我需要实现的新功能。

例如,我正在编写一个具有函数WillCollideWith(LineSegment的点类:

public class Point {
    // Point data and constructor ...

    public bool CollidesWithLine(LineSegment lineSegment) {
        Vector PointEndOfMovement = new Vector(Position.X + Velocity.X,
                                               Position.Y + Velocity.Y);
        LineSegment pointPath = new LineSegment(Position, PointEndOfMovement);
        if (lineSegment.Intersects(pointPath)) return true;
        return false;
    }
}

当我意识到我需要LineSegment.Intersects(LineSegment函数时,我正在为CollidesWithLine编写测试。但是,我是否应该停止测试周期中的工作以创建此新功能?这似乎违反了“红色,绿色,重构”原则。

我是否应该只编写检测到lineSegments在CollidesWithLine函数内部相交并在工作后对其进行重构的代码?在这种情况下,这是可行的,因为我可以从LineSegment访问数据,但是如果这种数据是私有的,那该怎么办?

Answers:


14

只需注释掉您的测试和最近的代码(或放入一个存储区),这样您实际上就将时间倒退到了周期的开始。然后从LineSegment.Intersects(LineSegment)测试/代码/重构开始。完成后,取消注释先前的测试/代码(或从存储中拉出)并保持循环。


然后忽略它然后再返回它又有什么不同呢?
约书亚·哈里斯

1
只是很小的细节:报告中没有额外的“忽略我”测试,如果您使用隐藏,则代码与“干净”情况是无法区分的。
哈维尔

什么是藏匿处?就像版本控制一样吗?
约书亚·哈里斯

1
一些VCS将其实现为功能(至少是Git和Fossil)。它使您可以删除更改,但可以保存以供稍后重新应用。手动操作并不难,只需保存一个差异并返回到最后一个状态即可。稍后,您重新应用diff并继续。
哈维尔2012年

6

在TDD周期上:

在“通过测试”阶段,应该编写最简单的实现通过测试的实现。为了使您的测试通过,您决定创建一个新的协作者来处理缺少的逻辑,因为放入点类以进行测试可能要花很多时间。这就是问题所在。我想你要通过的考试是一个很大的进步。因此,我认为问题出在测试本身中,您应该删除/注释该测试,并找出一个更简单的测试,该测试可以使您迈出第一步,而无需引入LineSegment.Intersects(LineSegment)部分。一个通过测试的人,然后可以重构通过将此新逻辑移至LineSegment.Intersects(LineSegment)方法中,您的代码(此处将应用SRP原理)。您的测试仍将通过,因为您不会更改任何行为,而只是移动了一些代码。

在当前的设计解决方案上

但是对我来说,您还有一个更深层次的设计问题,那就是您违反了单一责任原则。点的作用是……要点,仅此而已。毫无意义的是,只有x和y值。点是价值类型。对于细分而言,情况相同,细分是由两点组成的值类型。它们可以包含一些“智能”,例如根据其点的位置来计算其长度。就是这样。

现在,确定一个点和一个线段是否碰撞是一个整体的责任。对于一个点或段来说,要独自处理肯定是太多的工作。它不能属于Point类,因为否则Point会知道Segments。而且它不能属于细分市场,因为细分市场已经有责任照顾细分市场中的各个点,并可能还要计算细分市场本身的长度。

因此,此责任应归于另一个类,例如“ PointSegmentCollisionDetector”,该类应具有以下方法:

bool AreInCollision(Point p,Segment s)

这就是您将要与“点”和“线段”分开进行测试的地方。

这种设计的好处是,您现在可以对碰撞检测器进行不同的实现。因此,例如通过在运行时切换碰撞检测方法来对游戏引擎进行基准测试(假设您正在编写game:p)将很容易。或者在运行时在不同的碰撞检测策略之间进行一些视觉检查/实验。

目前,通过将这种逻辑放入您的Point类中,您正在锁定事物并在Point类上推卸过多责任。

希望有道理,


你是正确的,我是想测试太大的变化,我认为你是对有关分隔条件是出到碰撞类,但是这让我问了一个全新的问题,你也许能帮助我:我应该当方法仅相似时使用接口?
约书亚·哈里斯

2

以TDD方式执行的最简单的操作是为LineSegment提取接口,然后更改方法参数以接收该接口。然后,您可以模拟输入线段并独立编码/测试Intersect方法。


1
我知道这是我听到最多的TDD方法,但是ILineSegment没有意义。接口外部资源或以多种形式出现的东西是一回事,但是我看不到一个原因,我会将任何功能都附加到线段以外的任何东西上。
约书亚·哈里斯

0

使用jUnit4,您可以将@Ignore注释用于要推迟的测试。

将Annotation添加到要推迟的每个方法,然后继续编写所需功能的测试。回圈以在以后重构较旧的测试用例。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.