1. 程式人生 > >單元測試之Mock(Moq)

單元測試之Mock(Moq)

inter 商品 void 簡單 calc 不用 準備 targe ted

Mock翻譯為“嘲弄”,其實就是偽造一個對象用於測試。在單元測試中,被測試方法依賴於其他對象時,為了測試簡單一般“偽造”一個這個對象;這樣做的目的:

  • 不用考慮依賴對象的復雜性(方便準備測試數據)
  • 只專註測試被測試方法,不將單元測試擴充到測試依賴對象

    打折算法測試

    商場中的商品類:
    public class Product {
        public int ProductID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public string Category { set; get; }
    }

打折接口:

    public interface IDiscountHelper {
        decimal ApplyDiscount(decimal totalParam);
    }

被測試的打折算法

    public class LinqValueCalculator : IValueCalculator {
        private IDiscountHelper discounter;

        public LinqValueCalculator(IDiscountHelper discountParam) {
            discounter = discountParam;
        }

        public decimal ValueProducts(IEnumerable<Product> products) {
            return discounter.ApplyDiscount(products.Sum(p => p.Price));
        }

顯然,被測試方法ValueProducts依賴打折接口IDiscountHelper,這裏通過偽造一個實現IDiscountHelper接口的對象。

Moq功能介紹

  1. 創建偽造對象
    Mock<IDiscountHelper> mock = new Mock<IDiscountHelper>();
  2. 設置偽造對象的方法
    mock.Setup(m => m.ApplyDiscount(It.IsAny<decimal>()))
  3. 參數限制
    t.IsAny<decimal>()通過It限制這裏列出幾個

    限制表達式 解釋 例子
    It.Is 通過predicate返回bool值判斷限制測試 It.Is<decimal>(v => v == 0)
    It.IsAny T類型的任意值 It.IsAny()
    It.IsInRange T類型參數的值在範圍內或外(rangeKind參數決定) It.IsInRange<int>(1, 100, Range.Inclusive)
  4. 設置返回值
    .Returns<decimal>(total => total);

構造單元測試

    [TestClass]
    public class UnitTest2 {

        private Product[] createProduct(decimal value) {
            return new[] { new Product { Price = value } };
        }

        [TestMethod]
        [ExpectedException(typeof(System.ArgumentOutOfRangeException))]
        public void Pass_Through_Variable_Discounts() {
            // arrange
            Mock<IDiscountHelper> mock = new Mock<IDiscountHelper>();
            mock.Setup(m => m.ApplyDiscount(It.IsAny<decimal>()))
                .Returns<decimal>(total => total);
            mock.Setup(m => m.ApplyDiscount(It.Is<decimal>(v => v == 0)))
                .Throws<System.ArgumentOutOfRangeException>();
            mock.Setup(m => m.ApplyDiscount(It.Is<decimal>(v => v > 100)))
                .Returns<decimal>(total => (total * 0.9M));
            mock.Setup(m => m.ApplyDiscount(It.IsInRange<decimal>(10, 100,
                Range.Inclusive))).Returns<decimal>(total => total - 5);
            var target = new LinqValueCalculator(mock.Object);

            // act
            decimal FiveDollarDiscount = target.ValueProducts(createProduct(5));
            decimal TenDollarDiscount = target.ValueProducts(createProduct(10));
            decimal FiftyDollarDiscount = target.ValueProducts(createProduct(50));
            decimal HundredDollarDiscount = target.ValueProducts(createProduct(100));
            decimal FiveHundredDollarDiscount = target.ValueProducts(createProduct(500));

            // assert
            Assert.AreEqual(5, FiveDollarDiscount, "$5 Fail");
            Assert.AreEqual(5, TenDollarDiscount, "$10 Fail");
            Assert.AreEqual(45, FiftyDollarDiscount, "$50 Fail");
            Assert.AreEqual(95, HundredDollarDiscount, "$100 Fail");
            Assert.AreEqual(450, FiveHundredDollarDiscount, "$500 Fail");
            target.ValueProducts(createProduct(0));
        }
    }

單元測試之Mock(Moq)