单元测试之Mock(Moq)

Posted lovetomato

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单元测试之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返回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)的主要内容,如果未能解决你的问题,请参考以下文章

C#单元测试--如何使用moq.mock进行依赖注入

自动Mock,让编写单元测试更简单

Moq:Mock.Verify()抛出NullReferenceException

Moq + 单元测试 - System.Reflection.TargetParameterCountException:参数计数不匹配

单元测试 | 如何Mock IHttpClientFactory

.net测试篇之单元测试/集成测试神器Autofixture