C# 关于TDD的知识漏洞
我制作了一个列表(通过示例测试驱动开发中的所谓“测试列表”),我将从中选择一个要实现的测试 所以我启动了Visual Studio,创建了一个新的解决方案,为单元测试添加了一个项目,然后。。。我需要想出一个类,在这个类中,我将为我从列表中选择的测试放置测试方法 这就是我被卡住的地方。我如何知道我需要哪个类,如何命名它,如何知道它是否正确?这是需要事先考虑的事情吗 这是需要事先考虑的事情吗 事先。这就是为什么它称为测试驱动开发…C# 关于TDD的知识漏洞,c#,.net,tdd,C#,.net,Tdd,我制作了一个列表(通过示例测试驱动开发中的所谓“测试列表”),我将从中选择一个要实现的测试 所以我启动了Visual Studio,创建了一个新的解决方案,为单元测试添加了一个项目,然后。。。我需要想出一个类,在这个类中,我将为我从列表中选择的测试放置测试方法 这就是我被卡住的地方。我如何知道我需要哪个类,如何命名它,如何知道它是否正确?这是需要事先考虑的事情吗 这是需要事先考虑的事情吗 事先。这就是为什么它称为测试驱动开发… 在开始实施之前,您必须设计工作流程。您读过Kent Beck-TDD
在开始实施之前,您必须设计工作流程。您读过Kent Beck-TDD吗?不要试图提前把一切都解决掉。潜入,做点什么,让它工作,不管它是什么,然后你会对它应该是什么有更好的想法,你可以改变它。原则是,在考虑如何做之前,先考虑你想做什么。编写一个测试,测试您想做的事情,然后实现一个解决方案。第一次、第二次和第三次您都会出错,但是这个过程会使您更接近实际的解决方案,当您完成时,您应该拥有有价值的测试套件和一组松散耦合的类来完成工作 编辑以回应评论 不,不是随便取的名字。您需要预先执行一定量的设计。我通常从我认为解决方案需要的关键类型开始。然后我开始一个测试类(比如FooTest),在这个类中我为我希望Foo做的事情编写一个测试。我使用编写测试的过程来编写接口。Resharper在这方面很好,因为我可以引用尚不存在的类型和方法,并让Resharper创建它们:
[TestFixture]
public class FooTest
{
[Test]
public void Bar()
{
var foo = (IFoo)null; //At this point I use Resharper to create IFoo interface
Assert.IsTrue(foo.Bar()); //At this point I use Resharper to create bool IFoo.Bar();
}
}
显然,上面提到的将失败,因为有一个null ref ex,但是我有一个测试,我有一个与方法的接口。我可以继续遵循这个过程来建模我的解决方案,直到我准备好开发一个具体的实现为止。在这个过程之后,我重点关注接口和类型之间的交互,而不是这些类型的实现。一旦我构建了Foo,我只需将上面的内容更改为
var Foo=new Foo()编码>并使所有测试变为绿色。这个过程还意味着我为每个类都提供了一个接口,这在编写单元测试时是必不可少的,因为我可以使用动态模拟库轻松地模拟依赖项,比如。一个好主意是从您的领域而不是一些模糊的测试的角度来考虑这个问题。例如,您需要开发具有foo1和foo2功能的Foo
因此,您使用foo1Test
和foo2Test
创建了一个名为FooTest
的测试类。最初,这些测试会失败,而您只是努力让它们通过。您的系统是做什么的?你可以从那里开始
假设您正在编写一个功能,该功能读取包含给定帐户交易的文档,并生成借方和贷方的汇总摘要
让我们创建一个测试:
public class TransactionSummarizationTest {
@Test
public void summarizesAnEmptyDocument() {
TransactionSummarization summarizer = new TransactionSummarization();
Summary s = summarizer.summarizeTransactionsIn(new Scanner());
assertEquals(0.00, s.debits, 0.0);
assertEquals(0.00, s.credits, 0.0);
}
由于TransactionSummarization
和Summary
类还不存在,现在就创建它们。他们看起来是这样的:
TransactionSummarization.java
public class TransactionSummarization {
public Summary summarizeTransactionsIn(Scanner transactionList) {
return null;
}
}
Summary.java
public class Summary {
public double debits;
public double credits;
}
现在您已经处理了所有编译错误,可以运行测试了。由于summaryTransactionsin
方法的空实现,它将以NullPointerException
失败。从该方法返回一个摘要实例,并将其传递
public Summary summarizeTransactionsIn(Scanner transactionList) {
return new Summary();
}
再次运行测试并通过
public Summary summarizeTransactionsIn(Scanner transactionList) {
return new Summary();
}
既然你有了第一次考试,下一步怎么办?我想我应该用一个事务来尝试测试
@Test
public void summarizesDebit() {
TransactionSummarization summarizer = new TransactionSummarization();
Summary s = summarizer.summarizeTransactionsIn(new Scanner("01/01/12,DB,1.00"));
assertEquals(1.00, s.debits, 0.0);
assertEquals(0.00, s.credits, 0.0);
}
在运行测试之后,我们应该看到它失败了,因为我们没有累积值,只是返回一个新的摘要
public Summary summarizeTransactionsIn(Scanner transactionList) {
String currentLine = transactionList.nextLine();
txAmount = currentLine.split(",")[2];
double amount = Double.parseDouble(txAmount);
return new Summary(amount);
}
在修复了摘要中的编译错误并实现了构造函数之后,您的测试应该再次通过。下一个测试是什么?我们能学到什么?嗯,我对借记卡/贷记卡很好奇,所以我们下一步再做
@Test
public void summarizesCredit() {
TransactionSummarization summarizer = new TransactionSummarization();
Summary s = summarizer.summarizeTransactionsIn(new Scanner("01/01/12,CR,1.00"));
assertEquals(0.00, s.debits, 0.0);
assertEquals(1.00, s.credits, 0.0);
}
运行这个测试,我们会看到它失败,因为借方是1.00,而贷方是0.0。与我们想要的正好相反,但这完全是意料之中的,因为我们没有以任何方式检查事务类型。我们现在就开始吧
public Summary summarizeTransactionsIn(Scanner transactionList) {
double debits = 0.0;
double credits = 0.0;
String currentLine = transactionList.nextLine();
String[] data = currentLine.split(",");
double amount = Double.parseDouble(data[2]);
if("DB".equals(data[1]))
debits += amount;
if("CR".equals(data[1]))
credits += amount;
return new Summary(debits, credits);
}
现在所有测试都通过了,我们可以继续进行下一个测试。现在怎么办?我认为,如果我们想让这个项目成功,只处理文件中的一行对我们没有多大帮助。同时处理多个记录如何?让我们写一个测试
@Test
public void summarizesDebitsAndCredits() {
String transactions = "01/01/12,CR,1.75\\n" +
"01/02/12,DB,3.00\\n" +
"01/02/12,DB,2.50\\n" +
"01/02/12,CR,1.25";
TransactionSummarization summarizer = new TransactionSummarization();
Summary s = summarizer.summarizeTransactionsIn(new Scanner(transactions));
assertEquals(5.50, s.debits, 0.0);
assertEquals(3.00, s.credits, 0.0);
}
现在,运行我们所有的测试,我们看到这个测试以一种可预测的方式失败。它告诉我们,我们的借方是0.00,贷方是1.75,因为我们只处理了第一条记录
我们现在来解决这个问题。一个简单的while
循环,我们应该重新开始业务:
public Summary summarizeTransactionsIn(Scanner transactionList) {
double debits = 0.0;
double credits = 0.0;
while(transactionList.hasLine()) {
String currentLine = transactionList.nextLine();
String[] data = currentLine.split(",");
double amount = Double.parseDouble(data[2]);
if("DB".equals(data[1]))
debits += amount;
if("CR".equals(data[1]))
credits += amount;
}
return new Summary(debits, credits);
}
所有的考试都通过了,剩下的就交给你了。需要考虑的是边缘情况,例如包含混合情况的文件,例如“cr”与“cr”,或无效/缺失数据等
而且,在我输入了所有这些之后,我意识到你指的是C。不幸的是,我是用Java实现的,我懒得把它转换成C语言,但我希望这能有所帮助。:-)
谢谢
布兰登是的,我读过。你是不是建议我从给我的类取一个随机的名字开始,然后从那里开始计算?因为这是我面临的问题,我有一个测试列表,但没有上下文。因为似乎有两个学派。一是像在任何项目上一样做好准备,获取业务需求,收集用户故事,等等,然后从那里开始工作。另一种方法似乎是完全有机的。除了希望程序执行的测试之外,您什么都没有。第二种方法阻碍了我,因为它是h