C# TDD:编写可测试类的帮助
我有一个快速的小应用程序,想尝试使用TDD进行开发。我从未使用过TDD,事实上,直到我找到ASP.NET-MVC,我才知道它是什么。(我的第一个MVC应用程序有单元测试,但它们很脆弱,相互耦合,占用了太多的时间,被放弃了——我来学习单元测试!=TDD) 应用程序背景: 我有一个作为字符串读入的采购订单的文本转储。我需要解析文本并返回新采购订单号、新行项目号、旧采购订单号、旧采购订单行号。很简单 目前,我只处理新的采购订单详细信息(编号/行),并且有如下模型:C# TDD:编写可测试类的帮助,c#,asp.net-mvc,unit-testing,tdd,C#,Asp.net Mvc,Unit Testing,Tdd,我有一个快速的小应用程序,想尝试使用TDD进行开发。我从未使用过TDD,事实上,直到我找到ASP.NET-MVC,我才知道它是什么。(我的第一个MVC应用程序有单元测试,但它们很脆弱,相互耦合,占用了太多的时间,被放弃了——我来学习单元测试!=TDD) 应用程序背景: 我有一个作为字符串读入的采购订单的文本转储。我需要解析文本并返回新采购订单号、新行项目号、旧采购订单号、旧采购订单行号。很简单 目前,我只处理新的采购订单详细信息(编号/行),并且有如下模型: public class Purch
public class PurchaseOrder
{
public string NewNumber {get; private set;}
public string NewLine {get; private set;}
new public PurchaseOrder(string purchaseOrderText)
{
NewNumber = GetNewNumber(purchaseOrderText);
NewLine = GetNewLine(purchaseOrderText);
}
// ... definition of GetNewNumber / GetNewLine ...
// both return null if they can't parse the text
}
现在我想添加一个方法“IsValid”,该方法只有在“NewNumber”和“NewLine”都非null时才应该为true。所以我想像这样测试它:
public void Purchase_Order_Is_Valid_When_New_Purchase_Order_Number_And_Line_Number_Are_Not_Null()
{
PurchaseOrder order = new PurchaseOrder()
{
NewNumber = "123456",
NewLine = "001"
};
Assert.IsTrue(order.IsValid);
}
这很容易,但允许公共设置器和无参数构造函数似乎是一个糟糕的折衷方案。因此,另一种方法是在构造函数中输入一个“purchaseOrderText”值,但随后我还要测试“GetNewNumber”和“GetNewLine”的代码
我有点困惑于如何将其作为一个可测试类编写,同时试图将其锁定在对模型有意义的方面。这似乎是一个常见的问题,所以我想我只是缺少了一个明显的概念。与其公开设置器,不如将其内部化,然后在主项目中使您的测试程序集内部可见。这样,您的测试可以看到您的内部成员,但其他人无法看到 在你的主要项目中,放一些像这样的东西
[assembly: InternalsVisibleTo( "UnitTests" )]
其中,UnitTests是测试程序集的名称。一种解决方案是不让构造函数执行此工作:
public class PurchaseOrder
{
public PurchaseOrder(string newNumber, string newLine)
{
NewNumber = newNumber;
NewLine = newLine;
}
// ...
}
这样,测试就很容易,而且是孤立的-您不需要同时测试GetNewNumber
和GetNewLine
要帮助使用PurchaseOrder
,您可以创建一个将其组合在一起的工厂方法:
public static PurchaseOrder CreatePurchaseOrder(string purchaseOrderText)
{
return new PurchaseOrder(
GetNewNumber(purchaseOrderText),
GetNewLine(purchaseOrderText));
}
在测试项目中为类创建私有访问器,然后使用该访问器设置测试的属性。在VS中,您可以通过在类中单击鼠标右键,选择“创建专用访问器”,然后选择测试项目来创建专用访问器。在测试中,您可以这样使用它:
public void NameOfTest()
{
PurchaseOrder_Accessor order = new PurchaseOrder_Accessor();
order.NewNumber = "123456";
order.NewLine = "001";
Assert.IsTrue(order.IsValid);
}
如果您有默认构造函数,则可以执行以下操作:
public void NameOfTest()
{
PurchaseOrder order = new PurchaseOrder()
PurchaseOrder_Accessor accessor =
new PurchaseOrder_Accessor( new PrivateObject(order) );
accessor.NewNumber = "123456";
accessor.NewLine = "001";
Assert.IsTrue(order.IsValid);
}
我可能会创建一个新的构造函数PublicPurchaseOrder(stringNewNumber,stringNewLine)。依我看,无论如何你都可能需要它。有点离题:) 使用TDD,首先要编写单元测试,然后编写足够的代码通过测试,然后再进行重构。通过首先编写测试,您应该确切地知道如何编程以使代码可测试
这也是对单一责任原则的更好运用——从文本到数字和行项目的转换本质上是一种IO功能,而不是采购订单固有的功能。如果您的文本转储格式发生变化,您的采购订单就不需要更改。我要补充的是,这正是TDD最擅长揭示的设计实现——如果您无法对某些内容进行合理的测试,它可能做得太多了。谢谢您的回答和评论。我同意这两种观点,并且必须承认,看到TDD这样做我有点头晕(正如广告所宣传的那样)。这个程序非常简单,可以像一个主要的程序函数一样完成,但我认为你必须通过实践来学习,而不是仅仅阅读。我正在尝试这个。我的下一个测试是IsValid,这是一个很容易编写的测试。在我编写测试之后,很明显,我的代码无法支持测试,所以我要么需要重构,要么重写测试(但我不知道是哪个)。谢谢哇,我甚至不知道访问者。找到了关于它们的MSDN文章,它在我的待办事项列表中:)谢谢!