• 【Pro ASP.NET MVC 3 Framework】.学习笔记.3.MVC的主要工具-单元测试


    IProductRepository接口定义了一个仓库,我们通过它获得、更新Product对象。IPriceReducer接口指定了一个功能,它将要对所有的Products实施,通过一个参数,降低他们的价格。

    在这个例子中,我们的目的是创建一个IProductReducer的实现,它有以下功能:

    • 仓库中所有Product的价格都要减
    • 仓库的UpdateProduct方法可以被每个Product对象调用
    • 价格不能低于1

    为了帮助我们建立实现,我们创建一个FakeRepository类,它实现了IProductRepository接口。

    在ReducePrice方法上点右键,选择创建单元测试,在弹出对话框中选中要测试的方法,确定。单元测试会新建一个项目。

    这里用到Enumerable.Zip<TFirst, TSecond, TResult> 方法。下面有一个列子:

    1 int[] numbers = { 1, 2, 3, 4 }; 2 string[] words = { "one", "two", "three" }; 3 4 var numbersAndWords = numbers.Zip(words, (first, second) => first +""+ second); 5 6 foreach (var item in numbersAndWords) 7 Console.WriteLine(item); 8 9 // This code produces the following output: 10 11 // 1 one 12 // 2 two 13 // 3 three

    像拉链一样,对比两个集合中,相同Index的元素。

    1 [TestMethod] 2 publicvoid All_Prices_Are_Changed) { 3 4 // Arrange 5 FakeRepository repo =new FakeRepository(); 6 decimal reductionAmount =10; 7 IEnumerable<decimal> prices = repo.GetProducts().Select(e => e.Price); 8 decimal[] initialPrices = prices.ToArray(); 9 MyPriceReducer target =new MyPriceReducer(repo); 10 11 // Act 12 target.ReducePrices(reductionAmount); 13 14 prices.Zip(initialPrices, (p1, p2) => { 15 if (p1 == p2) { 16 Assert.Fail(); 17 } 18 return p1; 19 }); 20 }

    这是我们第一个单元测试,它包含我们让VS查看的属性。TestClass属性被应用到类上,TestMethod属性被用在任何包含一个单元测试的方法上。被VS忽略的方法不用包含此属性。

    你看到我们的单元测试方法遵循arrange/act/asert(A/A/A)模式。我们的单元测试方法叫做All_Prices_Are_Changed,通过Linq查询,调用ToArray得到FakeRepository类中包含的Product items的初始单价。然后,我们调用目标方法,使用Linq Zip方法,确保每个方法都改变了。如果任何元素没有改变,我们调用Asset.Fail方法,单元测试不及格。

    建立单元测试有很多不同的方法,这里有一个公共的方法,用一个单一且伟大的方法,测试所有的必须条件。我们更喜欢创建一些小的单元测试,每个值关注程序的一方面。我们的偏爱来自两个原因,第一个是当一个小的单元测试不及格,你会准确地知道你代码中的欠缺。第二个原因是在我们查看了所有测试原因后,倾向于在多个测试方法中以破旧的代码结束,我们比代码中关注的更多,但是我们找到一个简洁的程序A/A/A模式。

    1 ///<summary> 2 /// 检查优惠的总量是否相同 3 ///</summary> 4 [TestMethod] 5 publicvoid Correct_Total_Reduction_Amount() 6 { 7 FakeRepository repo =new FakeRepository(); 8 decimal reductionAmount =10; 9 decimal initialTotal = repo.GetTotalValue(); 10 MyPriceReducer target =new MyPriceReducer(repo); 11 12 //Act13 target.ReducePrices(reductionAmount); 14 15 //Assert16 Assert.AreEqual(repo.GetTotalValue(), (initialTotal - (repo.GetProducts().Count() * reductionAmount))); 17 } 18 19 ///<summary>20 /// 检查有没有单价低于1的产品 21 ///</summary>22 [TestMethod] 23 publicvoid No_Price_Less_Than_One_Dollar() 24 { 25 FakeRepository repo =new FakeRepository(); 26 decimal reductionAmount =decimal.MaxValue; 27 MyPriceReducer target =new MyPriceReducer(repo); 28 29 //Act30 target.ReducePrices(reductionAmount); 31 32 //Assert33 foreach(Product prod in repo.GetProducts()) 34 { 35 Assert.IsTrue(prod.Price >=1); 36 } 37 }

    这些方法都遵循相同的模式。我们创建一个FakeRepository对象,并手工将它注入到MyPriceReducer类的构造器中。然后调用ReducePrices方法,使用Assert类的方法检查结果。下面你可能用到的静态方法,来检查或报告测试的状态。

    Method Description
    AreEqual<T>(T,T) AreEqual<T>(T,T,string) AreNotEqual<T>(T,T) AreNotEqual<T>(T,T,string) 断言连个T类型的对象,是否有相同的值
    AreSame<T>(T,T) AreSame<T>(T,T,string)  AreNotSame<T>(T,T) AreNotSame<T>(T,T,string) 断言两个变量是否参照相同的对象
    Fail() Fail(string) 不及格一个断言,没有条件被检查
    Inconclusive() Inconclusive(string) 声明结果,当单元测试的结果不确定时
    IsTrue(bool) IsTrue(bool,string) 断言为true,总是用于等价一个返回bool结果的表达式
    IsFalse(bool) IsFalse(bool,string) 断言为false
    IsNull(object) IsNotNull(object,string) 断言变量参照自object
    IsInstanceOfType(object,Type) IsInstanceOfType(object,Type,string) IsNotInstanceOfType(object,Type) IsNotInstanceOfType(object,Type,string) 断言object是指定类型,或派生自指定类型

    Assert类中的每个静态方法,允许我们检查单元测试中的一些方面。当断言不及格时,抛出出异常,这意味着单元测试整体不及格。每个单元测试都被分别对待,所以其他测试会继续执行。

    每个方法都有一个带字符串参数的重载版本。字符串中包含断言不及格时,异常的message元素。AreEqual和AreNotEqual方法有一些重载来比较指定的类型。例如,这里有一个版本,它允许字符串被比较,不用考虑案例。

    ExecptionExpected属性比较奇怪。只有在单元测试抛出ExceptionType参数指定类型的异常,断言成功。这是一个方便的方法,确保异常抛出,不需要用try…catch块瞎搞。

    3 运行单元测试

    将单元测试的项目设为启动项目,VS会在当前解决方案中为TestClass和TestMethod扫描所有的类。

  • 相关阅读:
    电子发烧友 车用总线
    Leetcode 303. 区域和检索 数组不可变(中等) 304. 二维区域和检索 矩阵不可变(中等) 560. 和为K的子数组(中等) 前缀和数组求窗口和
    Leetcode 496. 下一个更大元素I(简单) 503. 下一个更大元素II(中等) 739. 每日温度(中等) 单调栈
    Leetcode 48. 旋转图像(中等) 54. 螺旋矩阵(中等) 59. 螺旋矩阵 II(中等) 旋转遍历二维数组
    Leetcode 76. 最小覆盖子串(困难) 567. 字符串的排列(中等) 438. 找到字符串中所有字母异位词(中等) 3. 无重复字符的最长子串(中等) 滑动窗口算法
    Leetcode 704. 二分查找(简单) 34. 在排序数组中查找元素的第一个和最后一个位置(中等) 二分查找以及边界查找
    Leetcode 239. 滑动窗口最大值(困难) 单调队列解决滑动窗口最大值
    Leetocde 370. 区间加法(中等) 1109. 航班预订统计(中等) 1094. 拼车(中等) 差分数组求频繁区间增减问题
    Leetcode 875. 爱吃香蕉的珂珂(中等) 1011. 在D天内送达包裹的能力(中等) 二分查找高级用法
    Leetcode 167. 两数之和 II 输入有序数组(中等) 344. 反转字符串(简单) 两端指针夹逼
  • 原文地址:https://www.cnblogs.com/msdynax/p/3281852.html
Copyright © 2020-2023  润新知