我试着把它应用到我的一个简单的项目中去学习TDD。以下是一些细节(以及前面的一个问题):
TDD: Help with writing Testable Class
具体来说,我有一个purchaseOrderCollection类,它有一个purchaseOrders的私有列表(在构造函数中传入),purchaseOrders有一个布尔属性isValid。purchaseOrderCollection有一个属性hasErrors,如果列表中的任何purchaseOrders都有效为false,则返回true。这就是我要测试的逻辑。
[TestMethod]
public void Purchase_Order_Collection_Has_Errors_Is_True_If_Any_Purchase_Order_Has_Is_Valid_False()
{
List<PurchaseOrder> orders = new List<PurchaseOrder>();
orders.Add(new PurchaseOrder(--some values to generate IsValid false--));
orders.Add(new PurchaseOrder(--some values to generate IsValid true--));
PurchaseOrderCollection collection = new PurchaseOrderCollection(orders);
Assert.IsTrue(collection.HasErrors);
}
这与我之前的问题类似,因为这个测试过于耦合,我必须知道是什么使得purchaseorder是有效的false或true才能通过测试,而实际上这个测试并不重要。问题是不同的(imo),因为类本身不是问题所在。
本质上,我希望能够声明一个有效的purchaseorder为false或true,而不必知道purchaseorder是什么。
从我有限的TDD知识来看,这是你使用存根或模拟的东西。我的主要问题是,这是正确的吗?或者我应该用不同的方法来做这个?或者我完全有缺陷,只是写了这个测试,却想错了?
我最初的想法是使用某种模拟框架并创建一个始终返回true或false的purchaseorder。从我读到的内容来看,我需要声明它是有效的虚拟的。所以我的第二个想法是修改它,将ipurchaseorder添加为purchaseorder的接口,然后创建一个总是返回false或true的伪purchaseorder。这两个想法都有效吗?
谢谢!
最佳答案
无论是创建存根还是模拟都是正确的。我更喜欢使用模拟框架。
使用模拟框架的工作原理是,您希望模拟PurchaseOrder类,从而抽象出它的实现。然后调用有效的设置预期,并在调用时返回此值。
使用Moq的示例,如果您使用的是C 3.0和.NET Framework 3.5:
[TestMethod]
public void Purchase_Order_Collection_Has_Errors_Is_True_If_Any_Purchase_Order_Has_Is_Valid_False()
{
var mockFirstPurchaseOrder = new Mock<IPurchaseOrder>();
var mockSecondPurchaseOrder = new Mock<IPurchaseOrder>();
mockFirstPurchaseOrder.Expect(p => p.IsValid).Returns(false).AtMostOnce();
mockSecondPurchaseOrder.Expect(p => p.IsValid).Returns(true).AtMostOnce();
List<IPurchaseOrder> purchaseOrders = new List<IPurchaseOrder>();
purchaseOrders.Add(mockFirstPurchaseOrder.Object);
purchaseOrders.Add(mockSecondPurchaseOrder.Object);
PurchaseOrderCollection collection = new PurchaseOrderCollection(orders);
Assert.IsTrue(collection.HasErrors);
}
编辑:
在这里,我使用了一个接口来创建purchaseorder的mock,但是您没有。您可以将isvalid标记为virtual并模拟purchaseorder类。我的经验法则是先用虚拟的。仅仅是为了创建一个接口,这样我就可以模拟一个对象而不需要任何架构上的原因,这对我来说是一种代码味道。