在TDD中,我必须首先编写“测试”还是“接口”?


23

我正在使用c#学习TDD,据我所知测试应该驱动开发,即在编写最基本的代码以通过测试之后再进行重构,然后首先编写一个失败的测试

但是也有人说“ 程序到接口,而不是实现 ”,因此请首先编写一个接口。这是我开始困惑的地方,如果我先写接口,那将违反两件事

  1. 为接口编写的代码不受test的驱动

  2. 这显然不是最低要求,我可以用一个简单的类编写它。

我也应该从编写接口测试开始吗?没有任何实现,我要测试什么?

如果这个问题听起来很傻,对此我感到非常困惑。可能是我从字面上讲。


8
“编程到接口”意味着将您需要的代码与完成代码分开。这并不意味着从字面上使用an interface。A class还提供了一个界面,因为您可以在private变量中隐藏实现细节。
2014年

@Doval,是的,您不需要所有接口,只有所谓的contract。例如,它可能以抽象类的形式出现,尽管它不应该是虚拟类/方法,因为您不应该实例化它。
trysis 2014年

2
TDD说:“编写失败的测试。” 一些严格的TDDer表示,如果您尚未编译测试,则它被视为“失败”,因为尚未声明其运行所在的数据类型。
所罗门慢

Answers:


29

您的第一个违规行为(“为接口编写的代码不受测试驱动。”)无效。让我们用一个简单的例子。假设您正在编写一个计算器类,并且正在编写一个加法运算。您可能会写什么测试?

public class CalculatorTest {
    @Test
    public void testAddTwoIntegers() {
        Calculator calc = new Calculator();
        int result = calc.add(2, 2)
        Assert.assertEquals(4, result);
    }
}

您的测试刚刚定义了接口。是add方法,看吗?add接受两个参数并返回它们的总和。您稍后可能会确定需要多个计算器,并在那时提取(在这种情况下)Java接口。由于您已经测试了该类公共接口,因此您的测试不应更改

从理论上讲,测试是系统的可执行规范。系统的接口应由该系统的用户驱动,而测试是定义交互的第一种方法。

我认为您不能将界面设计与测试设计区分开。定义交互并为其设计测试是相同的思维操作- 我将此信息发送到界面中时,我期望得到一定的结果。我的输入出现问题时,我预计会出现此错误。您可以在纸上进行此设计工作,然后从中编写测试,也可以同时进行测试-没关系。


2
+1“ 我认为您不能将界面设计与测试设计分开 ”,应以粗体显示,恕我直言:)
Binary Worrier 2014年

XML导入和CSV导入表示,如果您要测试一个功能性的多个实现,则显示起来甚至更容易,尽管实现会发生变化,但您可以从同一接口使用完全相同的方法测试它们。此外,测试通常涉及一些模拟,因此该接口是必需的。
Walfrat

您说测试不需要更改,但是new Calculator()实现对吗?如果需要一个新的实现,那么您可能会做一个MultiplicationCalculator,然后需要更改测试以new AdditionCalculator()使其仍然通过?还是我错过了什么?
史蒂夫·查麦拉德

3
@SteveChamaillard当然,如果您的设计将类名从Calculator更改为AdditionCalculator,则测试必须进行更改以匹配。当然,通过执行TDD,实际会发生的事情是您将首先更改测试,然后进行类更改以使测试通过。
埃里克·金

5

编写时我们在做什么interface?我们是在编写代码,还是在设计?

我不喜欢测试驱动设计的概念,但是我喜欢测试驱动开发。就个人而言,当我在编写测试之前通过设计接口来预先设计类时,我已经获得了最好的结果。我不认为接口是代码。该接口是我将使用TDD实现的设计。当我工作时,它可能会改变发展趋势,但这是我的路线图(以及测试清单)。

我会在咆哮之前停下来,但希望这对您有所帮助。


4

在TDD中,我必须首先编写“测试”还是“接口”?

这完全取决于您想做TDD的正统/宗教信仰。

我正在学习TDD

由于您正在学习,因此应该尝试获得适合您的个人工作流程。

  1. 如果您想按照书中的说明进行操作,则首先要编写一个测试,显然会失败,因为您根本没有任何代码。然后,编写一些代码以使测试通过。如果这样做,您可以自由地重构现有代码,因为您进行了一项测试,该测试为重构提供了某种安全网。决定使用接口是某种重构。

  2. 除了TDD以外:是否使用接口的问题首先就没有意义。当然,如果您确定要跨多个对象使用不同的行为,那么考虑使用接口是很有意义的:例如,如果您有某种输出到不同的目的地,则可以通过以下方式实现接口Writer并具有不同的输出类(FileWriterPrinter等)。虽然写接口是很普遍的说法,但这并不意味着:对所有东西都使用接口。有时,它是很多事物之间的一种间接的层次。顺便说一句。服务也是如此。但这是一个不同的话题。

  3. 另一方面,您可以通过另一种方式开发测试驱动程序:设计代码以实现可测试性。这意味着您编写了代码,这很容易测试-尽管事后再编写测试。不管您事先编写测试还是之后编写测试,都没关系,只要您仍然进行测试即可。


5
不能同意最后一点“只要事先进行测试就可以先编写测试还是事后编写测试都没有关系”。如果您碰巧在事后编写测试,则无法确定测试是否在测试正确的东西。
k4vin

4
正如我所说的...这取决于您的正统程度...
Thomas Junk

2

TDD或BDD意味着先做您的域接口,然后根据我的解释对它们进行测试。接口的实现具有预期的行为。

它仍然在代码之前进行测试,因为接口不包含可测试的逻辑,这是您编写测试所针对的结构。

我会做如下

  1. 编写半形式行为(给出:何时:然后:)

  2. 编写接口(以托管行为封装方法)

  3. 编写它标识的测试(输入给定,调用when,然后测试then)

  4. 编写/更改混凝土(实现接口的类)以通过测试


0

在设计接口之前,切勿编写测试。当您考虑编写哪种测试(测试设计)时,也不应同时设计(架构)您的应用程序。不要同时考虑两件事。您听说过关注点分离吗?它不仅适用于代码的物理结构,还适用于您的思考过程。

确定应如何首先设计应用程序。这意味着您需要设计接口以及这些接口之间的关系。在完成此操作之前,您不应该开始考虑测试。一旦知道了接口是什么,就可以先创建它们,然后针对它们编写测试,或者先编写测试然后创建它们。在后一种情况下,显然您将无法编译测试。在测试之前创建接口时,我认为没有任何危害,也没有违反TDD理念。


最佳答案中的推理看起来更引人注目:“我认为您不能将界面设计与测试设计分开。定义交互和为其设计测试是相同的思维操作- 我将此信息发送到界面中时,我希望得到一定的结果。我的输入有问题时,我希望此错误...”
咬到

@gnat我相信Nissam在这里指的是C#interface关键字,而不是通用术语“接口”。
RubberDuck

@RubberDuck我也这样认为,我认为这是一种糟糕的方法。“在您完成此操作之前,您不应该开始考虑测试……”正如我所写的那样,无论是在一般的界面意义上还是在具体的关键词意义上,在最高答案中进行推理似乎都更具说服力
-gna

公平的@gnat,您的评论中还不清楚。我个人在这里同意尼撒姆的观点。我发现它阻止了人们将TDD用作根本不进行设计的借口。YMMV。
RubberDuck

-2

只要将接口/代码/测试合并到项目中,就可以同时编写接口/代码/测试。

除非您的老板对TDD持虔诚态度,否则在这种情况下,您可能必须编写空接口->测试->最小代码(无意义的步骤)->更多测试->更多无意义的代码->更多测试->最后编写真实代码- >完成。

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.