我使用的是VS 2012,但这并不重要。
重要的是我通过首先编写所有测试然后创建代码来尝试做一些TDD。
但是,应用程序无法编译,因为我的对象或方法都不存在。
现在,在我看来,我应该能够创建我的所有测试,但仍然运行我的应用程序,以便我可以调试等。测试不应该阻止编译,因为缺少对象和方法。
我认为重点在于,在开发测试时,您可以开始看到重复等,这样您就可以在编写一行代码之前进行重构。
所以问题是,有没有办法做到这一点,还是我做错了?
修改 我正在使用VS2012和C#
答案 0 :(得分:3)
测试驱动开发是一个非常小的迭代。您没有预先定义所有测试。您可以根据一个需求的一个部分创建一个测试。然后实现代码以传递该测试。一旦它通过,你就可以处理另一部分需求。
这个想法是尝试预先完成所有设计(无论是创建详细的类图还是创建一系列测试)意味着你会发现改变设计中的弱点太昂贵了,所以你赢了'改善您的代码。
这是一个例子。假设你决定使用继承来关联两个对象,但是当你开始实现这些对象时,你会发现它们很难对它们进行测试。您发现单独测试每个对象会更容易,并通过包含来关联它们。正在发生的事情是测试正在以更松散的方向推动您的设计。这是TDD的一个非常好的结果 - 您正在使用测试来改进设计。
如果您提前编写了所有测试,假设您的继承设计决定是一个不错的选择,那么您可能会丢掉大量工作,或者您会说“现在做出类似改动太难了,所以我只会接受这种次优设计。“
您当然可以提前创建与业务规则相关的验收测试。这些被称为行为测试(行为驱动开发或BDD的一部分),从用户的角度来看它们很适合测试软件的功能。但那些不是单元测试。单元测试用于从开发人员的角度测试代码。提前创建单元测试会破坏TDD的目的,因为它会使测试更加困难,它会阻止您改进代码,并且经常会导致反叛和实践失败。这就是为什么做正确的事情很重要。
答案 1 :(得分:3)
我看到
的一个小问题首先编写所有测试,然后创建代码。
您不需要首先编写 ALL 您的测试,只需要一个,让它失败,让它通过并重复。这意味着理想情况下你应该理想地一个失败的测试。
在这种意义上,编译失败会被视为失败的测试。所以下一步是让它通过 - 即添加存根或返回默认值以使其编译。然后测试将是红色的..然后努力使其变为绿色。
答案 2 :(得分:3)
重要的是我通过先编写所有测试然后创建代码来尝试做一些TDD。
问题是“首先编写我的所有测试”最重要的是不“做某些TDD”。测试驱动的开发包括许多小的重复“红 - 绿 - 重构”循环:
如果您预先编写整个巨大的测试套件,那么您将永远花费在尝试进入“绿色”(所有测试通过)状态。
但是,应用程序无法编译,因为我的对象或方法都不存在。
这是任何编译语言的典型代表;这本身并不是TDD问题。这意味着,为了观察新测试失败,您可能必须为当前正在处理的任何功能编写最小存根,以使编译器满意。
例如,我可能会编写此测试(使用NUnit):
[Test]
public void DefaultGreetingIsHelloWorld()
{
WorldGreeter target = new WorldGreeter();
string expected = "Hello, world!";
string actual = target.Greet();
Assert.AreEqual(expected, actual);
}
然后我必须编写这么多代码来让应用程序编译并使测试失败:
public class WorldGreeter
{
public string Greet()
{
return String.Empty;
}
}
一旦我获得了构建解决方案并且我看到了一个失败的测试,我可以添加代码来进行第一次测试:
public string Greet()
{
return "Hello, world!";
}
一旦所有测试通过,我可以查看测试中的系统,看看可以采取哪些措施来改进设计。但是,在玩重构之前,TDD规则必须经历“红色”和“绿色”步骤。
我认为它的重点在于,在开发测试时,您可以开始看到重复等,以便在编写单行代码之前可以重构。
Martin Fowler将refactoring定义为“重构现有代码的一种规范技术,改变其内部结构而不改变其外部行为”(强调补充)。如果你还没有编写一行代码,就没有什么可以重构的。
所以问题是,有没有办法做到这一点,还是我做错了?
如果您正在寻找TDD,那么,是的,我担心您做错了。您可能能够提供出色的代码来完成您正在做的事情,但它不是TDD;这是一个问题是由你自己决定的。
答案 3 :(得分:2)
否。它的编码足以验证所需用例的实现
您可以尽早定义测试用例,但是为了对测试用例进行编码,您可以迭代地编写测试,让它失败。然后编写一些代码来确保代码通过。
然后冲洗并重复,直到覆盖所有测试用例,
编辑以发表评论。
在构建代码时,识别编程设计和故障的位置。极限编程使您能够自行更改代码,因为测试基础可以保护您的需求。您的意图是好的,但实际情况是,当您通过构建代码和测试基础发现设计问题和缺陷时,您将重构/重新设计测试测试用例。
然而,恕我直言,在一个非常一般的情况下,一个不编译的测试实际上是一个元测试失败,需要在继续之前进行纠正。它告诉你写一些代码!
答案 4 :(得分:2)
您应该能够使用存根函数创建空类,不是吗?
class Whatever {
char *foo( const char *name ) {}
int can_wibble( Bongo *myBongo ) {}
}
然后你可以编译。
答案 5 :(得分:0)
使用来自Wikipiedia的模拟:模拟对象是模拟对象,它们以受控方式模仿真实对象的行为。程序员通常会创建一个模拟对象来测试其他对象的行为,就像汽车设计师使用碰撞测试假人来模拟人类在车辆撞击中的动态行为一样。
请参阅this。
答案 6 :(得分:0)
我发现这对于尚不存在的对象使用动态: https://coderwall.com/p/0uzmdw