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
原文地址:https://www.cnblogs.com/kai364/p/5999723.html