• Moq基础


    一、概念

      Moq是利用诸如Linq表达式树和Lambda表达式等·NET 3.5的特性,为·NET设计和开发的Mocking库。Mock字面意思即模拟,模拟对象的行为已达到欺骗目标(待测试对象)的效果.
      Moq模拟类类型时,不可模拟密封类,不可模拟静态方法(适配器可解决),被模拟的方法及属性必须被virtual修饰.

    二、示例

     1 //待模拟对象
     2 public interface ITaxCalculate
     3 {
     4     decimal GetTax(decimal rawPrice);
     5 }
     6 
     7 public class Product
     8 {
     9     public int Id { get; set; }
    10 
    11     public string Name { get; set; }
    12 
    13     public decimal RawPrice { get; set; }
    14 
    15     //目标方法
    16     public decimal GetPriceWithTax(ITaxCalculate calc)
    17     {
    18         return calc.GetTax(RawPrice) + RawPrice;
    19     }
    20 }
    21 
    22 //单元测试
    23 [TestMethod]
    24 public void TestGetTax()
    25 {
    26     Product product = new Product
    27     {
    28         Id = 1,
    29         Name = "TV",
    30         RawPrice = 25.0M
    31     };
    32 
    33     //创建Mock对象,反射构建模拟对象空框架
    34     Mock<ITaxCalculate> fakeTaxCalculator = new Mock<ITaxCalculate>();
    35 
    36     //模拟对象行为
    37     fakeTaxCalculator.Setup(tax => tax.GetTax(25.0M)).Returns(5.0M);
    38 
    39     //调用目标方法
    40     decimal calcTax = product.GetPriceWithTax(fakeTaxCalculator.Object);
    41 
    42     //断言
    43     Assert.AreEqual(calcTax, 30.0M);
    44 }

     三、Mock方法

    1. Mock构造方法
      Mock构造方法主要存在两种重载,无参以及传入参数MockBehavior,Mock默认行为:MockBehavior.Loose.
      MockBehavior.Strict:对象行为未设置时调用抛出异常,示例如下.
      MockBehavior.Loose:对象行为未设置时调用不抛出异常,如有必要返回控制,如:0,null.
      MockBehavior.Default:等同于Loose.
      1 //构造方法
      2 public Mock();
      3 public Mock(MockBehavior behavior);
      4 
      5 //Strict示例
      6 Mock<IOrder> order = new Mock<IOrder>(MockBehavior.Strict);
      7 order.Object.ShowTitle(string.Empty);
    2. MockFactory
      Mock工厂,构建MockFactory时传入MockBehavior,通过Create方法创建Mock,次方法类似Mock构造方法.
      1 MockFactory factory = new MockFactory(MockBehavior.Loose);
      2 Mock<IOrder> order = factory.Create<IOrder>();
    3. Setup
      模拟对象行为方法,模拟出的方法与原有业务无关
       1 //模拟接口
       2 Mock<ICustomer> icustomer = new Mock<ICustomer>();
       3 //模拟普通方法
       4 icustomer.Setup(p => p.AddCall());
       5 icustomer.Setup(p => p.GetCall("Tom")).Returns("Hello");
       6 
       7 //模拟含有引用、输出参数方法
       8 string outString = "00";
       9 icustomer.Setup(p => p.GetAddress("", out outString)).Returns("sz");
      10 icustomer.Setup(p => p.GetFamilyCall(ref outString)).Returns("xx");
      11 
      12 //模拟有返回值方法
      13 icustomer.Setup(p => p.GetCall(It.IsAny<string>())).Returns((string s) => "Hello " + s);
      14 
      15 //模拟类
      16 Mock<Customer> customer = new Mock<Customer>();
      17 //模拟属性
      18 customer.Setup(p => p.Name).Returns("Tom");
      19 Assert.AreEqual("Tom", customer.Object.Name);
      20 
      21 //另一种方法模拟属性
      22 customer.SetupProperty(p => p.Name, "Tom2");
      23 Assert.AreEqual("Tom2", customer.Object.Name);
      24 
      25 //模拟类方法
      26 customer.Setup(p => p.GetNameById(1)).Returns("2");
      27 Assert.AreEqual("2", customer.Object.GetNameById(1));

      It用于添加参数约束,它有以下几个方法:
          Is<T>:匹配给定符合规则的值
          IsAny<T>:匹配给定类型的任何值
          IsRegex<T>:正则匹配
          IsInRange<T>:匹配给定类型的范围

       1 //对同一个动作可以模拟多个行为,执行动作时,从后往前依次匹配,直到匹配到为止
       2 var customer = new Mock<ICustomer>();
       3 customer.Setup(p => p.SelfMatch(It.IsAny<int>())).Returns((int k) => "任何数" + k);
       4 Console.WriteLine(customer.Object.SelfMatch(100));
       5 
       6 customer.Setup(p => p.SelfMatch(It.Is<int>(i => i % 2 == 0))).Returns("偶数");
       7 Console.WriteLine(customer.Object.SelfMatch(6));
       8 
       9 customer.Setup(p => p.SelfMatch(It.IsInRange<int>(0, 10, Range.Inclusive))).Returns("10以内的数");
      10 Console.WriteLine(customer.Object.SelfMatch(8));
      11 
      12 Console.WriteLine(customer.Object.SelfMatch(18));
      13 Console.WriteLine(customer.Object.SelfMatch(99));
      14 
      15 customer.Setup(p => p.ShowException(It.IsRegex(@"^d+$"))).Throws(new Exception("不能是数字"));
      16 customer.Object.ShowException("e1");
    4. Callback
      该方法用于模拟方法执行后回调执行,配合Setup使用
      1 Mock<ICustomer> customer = new Mock<ICustomer>();
      2 customer.Setup(p => p.GetCall(It.IsAny<string>()))
      3     .Returns("方法调用")
      4     .Callback((string s) => Console.WriteLine("OK " + s));
      5 customer.Object.GetCall("x");
    5. Throws
      抛出异常,配合Setup使用
      1 Mock<ICustomer> customer = new Mock<ICustomer>();
      2 customer.Setup(p => p.ShowException(string.Empty)).Throws(new Exception("参数不能为空!"));
      3 customer.Object.ShowException("");
    6. Verify、VerifyAll
      验证模拟的方法是否被执行。示例中可通过Verify验证模拟的tax.GetTax(25.0M)是否在Product中被执行
       1 //Verifiable标记
       2 Mock<ICustomer> customer = new Mock<ICustomer>();
       3 customer.Setup(p => p.GetCall(It.IsAny<string>())).Returns("方法调用").Verifiable();
       4 //若不执行此句代码则验证失败
       5 customer.Object.GetCall("");
       6 customer.Verify();
       7 
       8 //Verify验证
       9 customer.Setup(p => p.GetCall());
      10 customer.Object.GetCall();
      11 //Verify方法表明该动作一定要在验证之前执行,若调用verify之前都没执行则抛出异常
      12 customer.Verify(p => p.GetCall());
      13 
      14 //添加次数验证
      15 customer.Object.GetCall();
      16 customer.Verify(p => p.GetCall(), Times.AtLeast(2), "至少应被调用2次");
      17 
      18 //验证所有被模拟的动作是否都被执行,无论是否标记为Verifiable
      19 customer.VerifyAll();
    7. As
      向Mock中条件一个接口实现,只能在对象的属性、方法首次使用之前使用,且参数只能是接口,否则抛出异常。
      之前一直不知道As方法存在有什么意义,虽然调试时监测对象信息可以看到两个Mock之间关联的痕迹,但是一直不知道有什么用,该怎么用,直到今天看到一篇博客...
       1 public interface IFirstInterface
       2 {
       3     int SomeMethodOnFirstInterface();
       4 }
       5 
       6 public interface ISecondInterface
       7 {
       8     int SomeMethodOnSecondInterface();
       9 }
      10 
      11 public interface SomeClassImplementingInterfaces : IFirstInterface, ISecondInterface
      12 {
      13 }
      14 
      15 public class SomeClass
      16 {
      17     public static int MultipleInterfaceUser<T>(T x)
      18         where T : IFirstInterface, ISecondInterface
      19     {
      20         IFirstInterface f = (IFirstInterface)x;
      21         ISecondInterface s = (ISecondInterface)x;
      22 
      23         return f.SomeMethodOnFirstInterface() + s.SomeMethodOnSecondInterface();
      24     }
      25 }
      26 
      27 //测试代码
      28 Mock<SomeClassImplementingInterfaces> c = new Mock<SomeClassImplementingInterfaces>();
      29 
      30 Mock<IFirstInterface> firstMock = c.As<IFirstInterface>();
      31 firstMock.Setup(m => m.SomeMethodOnFirstInterface()).Returns(2);
      32 
      33 Mock<ISecondInterface> secondMock = firstMock.As<ISecondInterface>();
      34 secondMock.Setup(m => m.SomeMethodOnSecondInterface()).Returns(4);
      35 
      36 int returnValue = SomeClass.MultipleInterfaceUser<SomeClassImplementingInterfaces>(c.Object);
      37 
      38 Assert.AreEqual(returnValue, 6);

    四、参考链接

    • http://blog.csdn.net/alicehyxx/article/details/50667307
    • http://www.cnblogs.com/wintersun/archive/2010/09/04/1818092.html
  • 相关阅读:
    BZOJ2301——莫比乌斯&&整除分块
    2019HDU多校第五场A fraction —— 辗转相除法|类欧几里得
    AKS素性检测
    2019牛客多校B generator 1——十进制快速幂
    BZOJ 3884——欧拉降幂和广义欧拉降幂
    libevent HTTP client 的实现
    google proto buffer安装和简单示例
    setenv LD_LIBRARY_PATH
    Centos6.4下安装protobuf及简单使用
    lrzsz
  • 原文地址:https://www.cnblogs.com/kai364/p/5999723.html
Copyright © 2020-2023  润新知