前进中的蜗牛

番茄大叔

水滴穿石,非一日之功;没有量变,何来质变。

单元测试之Mock(Moq)

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) 通过predicate返回bool值判断限制测试 It.Is<decimal>(v => v == 0)
    It.IsAny() T类型的任意值 It.IsAny()
    It.IsInRange(TValue from, TValue to, Range rangeKind) 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));
        }
    }
posted @ 2018-10-24 11:53  LoveTomato  阅读(710)  评论(0编辑  收藏  举报