二、为某一个类上的某一个公开方法编写单元测试
当类以及类之间关系设计好之后,就开始根据业务功能的需要,逐步设计类的成员以及方法以实现这个类的功能。而在设计一个类的方法时,则是根据业务需求的要求来设计的,因此在编写一个类中的公开方法时,对这个类需要达成什么样的效果,应该是非常明确的。在明确了目标之后,其实编写单元测试就已经可以实现了,尽管现在实现代码还根本不存在。根据上面的例子,我们首先来编写ProductOrder类的Count方法的单元测试,此时,Count方法是没有真正实现的,甚至Count方法都还不存在(在单元测试代码中使用dynamitic关键字调用不存在的Count方法)。
ProductOrder类的Count方法,其实是统计所有单据中包含的所有货物的价格,因此可以分析得知Count方法依赖于BaseOrderDetail类的Count方法,并且属性OrderDetails会包含多个BaseOrderDetail类,这意味着在编写单元测试时,我们需要把这些BaseOrderDetail类都使用Mock对象代替。
Unit Test for Count /// <summary> /// A test for the method Count ///</summary> [TestMethod()] public void TestCount() { // Arrange dynamic target = new ProductOrder(); decimal expected = 8748.55m; // Mock a jacket order detail and set return value of the method Count(), no matter real price and amount. Mock<BaseOrderDetail> mockJacketOrderDetail = new Mock<BaseOrderDetail>(); mockJacketOrderDetail.Setup(e => e.Count()).Returns(350.55m); // Mock a iPAD order detail and set return value of the method Count(), no matter real price and amount. Mock<BaseOrderDetail> mockiPadOrderDetail = new Mock<BaseOrderDetail>(); mockiPadOrderDetail.Setup(e => e.Count()).Returns(3499.00m); // Mock a iPAD order detail and set return value of the method Count(), no matter real price and amount. Mock<BaseOrderDetail> mockNotebookDetail = new Mock<BaseOrderDetail>(); mockNotebookDetail.Setup(e => e.Count()).Returns(4899.00m); target.OrderDetails = new List<BaseOrderDetail>(); target.OrderDetails.Add(mockJacketOrderDetail.Object); target.OrderDetails.Add(mockiPadOrderDetail.Object); target.OrderDetails.Add(mockNotebookDetail.Object);< // Acction decimal actual = target.Count(); // Assert Assert.AreEqual(actual, expected); } |
在这个单元测试代码中,我们因为估计到实现Count方法需要依赖于OrderDetails属性,并合计里面所有BaseOrderDetail对象的价格,而BaseOrderDetail的价格是通过BaseOrderDetail.Count()方法获得的。我们只是测试ProductOrder的Count()方法,因此不需要再深入思考BaseOrderDetail.Count是如何实现的,只需要对BaseOrderDetail.Count()方法进行Mock即可。
因此在这段代码中,添加了三个Mock的BaseOrderDetail对象,并分别设置他们的Count()方法返回值,最后把它们都加入到ProductOrder的OrdeDetails属性中。到此,这个单元测试就写完了,但其实此时,我们的Count方法还根本没实现。所以运行单元测试会抛出异常。
编写方法的真正实现逻辑
那么,接下来,我们来实现这个方法的实际逻辑。
/// <summary> /// Count the total of the all orders. /// </summary> /// <returns></returns> public decimal Count() { decimal result; result = this.OrderDetails.Sum(e => e.Count()); return result; } |