记学习Moq框架(一) ,上一篇简单讲解了用法,这篇深入讲解用法
应用场景 示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public interface IProductService { void Add (Product product ) ; Task AddAsync (Product product ) ; void Delete (string id ) ; Task DeleteAsync (string id ) ; Product Get (string id ) ; Task<Product> GetAsync (string id ) ; }
Mock方法调用 单次方法调用 1 mock.Setup(p => p.Delete(It.IsAny<string >()));
同时也支持异步方法
1 mock.Setup(p => p.DeleteAsync(It.IsAny<string >()));
多次方法调用 1 2 3 4 mock.SetupSequence(p => p.Get(It.IsAny<string >()) .Returns(new Product(){Name = "618狂欢" }) .Returns(new Product(){Name = "双十一" }) .Returns(new Product(){Name = "年货节" });
也可以调用没有返回值的方法void Delete(string id)
1 2 3 4 mock.SetupSequence(p => p.Delete(It.IsAny<string >()) .Pass() .Pass() .Pass();
序列调用 由于多个方法按照序列调用,最好采用MockBehavior.Strict
严格模式
1 2 3 4 5 6 var mock = new Mock<IProductService>(MockBehavior.Strict);var sequence = new MockSequence();mock.InSequence(sequence).Setup(p => p.Delete("1" )); mock.InSequence(sequence).Setup(p => p.Delete("2" ));
1 2 3 4 5 6 var mock = new Mock<IProductService>(MockBehavior.Strict);var sequence = new MockSequence();mock.InSequence(sequence).Setup(p => p.DoWithString(It.IsAny<string >())); mock.InSequence(sequence).Setup(p => p.DoWithInteger(It.IsAny<int >()));
1 2 3 4 5 6 7 var mockFirst = new Mock<IProductService>(MockBehavior.Strict);var mockSecond = new Mock<IProductRepository>(MockBehavior.Strict);var sequence = new MockSequence();mockFirst.InSequence(sequence).Setup(p => p.Delete("1" )); mockSecond.InSequence(sequence).Setup(p => p.Get("1" ));
Mock方法参数 指定对象方法的参数类型
1 mock.Setup(p => p.Get(It.IsAny<string >()));
指定对象方法的参数值
1 mock.Setup(p => p.Get("1911112346550025" ));
正则表达式匹配It.IsRegex
1 mock.Setup(p => p.Get(It.IsRegex("[A-Z_]+" )));
两数区间It.IsInRange
1 mock.Setup(p => p.GetById(It.IsInRange(0 , 100 , Moq.Range.Exclusive)));
指定返回参数类型的表达式It.Is
1 mock.Setup(p => p.Get(It.Is<string >(s => s.StartsWith("19111" ))));
ref 参数 1 mock.Setup(p => p.DoSomething(ref It.Ref<string >.IsAny));
指定引用参数
1 2 var value = "This is a test value" ;mock.Setup(p => p.DoSomething(ref value ));
调用方法后执行回调方法
1 2 3 4 5 delegate void DoSomethingCallback (ref string value ) ;var newValue = "This is the new referenced value" ;mock.Setup(p => p.DoSomething(It.Ref<string >.IsAny)) .Callback(new DoSomethingCallback((ref string value ) => value = newValue));
out 参数 1 2 var value = "This is the expected out value" ;mock.Setup(p => p.DoSomething(out value ));
可选参数 1 2 3 4 5 6 7 public void DoSomething (string operation = "DEFAULT_OPERATION" ) { }service.DoSomething("MY_CUSTOM_OPERATION" ); service.DoSomething();
异步方法调用通常会需要CancellationToken
作为入参,给一个默认值default
即可
1 2 mock.Setup(p => p.DoSomethingAsync(It.IsAny<string >(), It.IsAny<CancellationToken>())); mock.Setup(p => p.DoSomethingAsync(It.IsAny<string >(), default ));
Mock方法返回 异步方法返回
1 2 mock.Setup(p => p.GetValueAsync()).Returns(Task.FromResult(123 )); mock.Setup(p => p.GetValueAsync()).ReturnsAsync(123 );
通过委托方法执行返回
1 2 3 4 5 mock.Setup(p => p.Add(It.IsAny<int >(), It.IsAny<int >()) .Returns((int first, int second) => first + second); mock.Setup(p => p.AddAsync(It.IsAny<int >(), It.IsAny<int >()) .ReturnsAsync((int first, int second) => first + second);
返回null
1 2 var mock = new Mock<Product>();mock.Setup(p => p.Img).Returns(null as string );
Mock方法回调 可以在方法后面链接Callback,来触发自定义的代码
1 2 var mock = new Mock<IProductService>();mock.Setup(p => p.Delete()).Callback(() => TestContext.Progress.Writeline("Here" ));
无入参的Action
1 mock.Setup(p => p.Delete(It.IsAny<string >())).Callback(() => TestContext.Progress.Writeline($"Incoming call" ));
也可以带入方法参数
1 mock.Setup(p => p.Delete(It.IsAny<string >())).Callback((string id) => TestContext.Progress.Writeline($"Incoming call: {id} " ));
属性方法
1 2 3 4 5 6 7 var mock = new Mock<Product>();mock.SetupGet(p => p.Img) .Callback(() => TestContext.Progress.Writeline("Getter invoked" )); mock.SetupSet(p => p.Img = It.IsAny<string >()) .Callback((string img) => TestContext.Progress.Writeline($"Setter received value: {img} " ));
中途变更状态值
1 2 3 4 5 string value = string .Empty;var mock = new Mock<IProductService>();mock.Setup(p => p.Delete(It.IsAny<string >())) .Callback((string id) => value = id);
特定条件
1 2 int counter = 0 ;mock.Setup(p => p.DoSomething()).Callback(() => { if (counter++ >= 5 ) throw new Exception(); });
链接多个委托
1 2 3 4 5 mock.Setup(p => p.DoSomething(It.IsAny<int >())).Callback((int value ) => { DoSomething(); DoSomethingWithValue(value ) });
抛出异常 1 2 3 4 var mock = new Mock<IProductService>();mock.Setup(p => p.Delete(It.IsAny<string >())) .Callback(() => throw new Exception());
Throws
语法糖
1 2 3 4 5 6 7 8 mock.Setup(p => p.Delete(It.IsAny<string >())) .Throws(new Exception("My custom exception" )); mock.Setup(p => p.Delete(It.IsAny<string >())) .ThrowsAsync(new Exception("My custom exception" )); mock.Setup(p => p.Delete((It.IsAny<string >())) .Throws<Exception>();
序列调用
1 2 3 4 5 6 7 mock.SetupSequence(p => p.GetSomeValue()) .Returns(1 ) .Throws<Exception>(); mock.SetupSequence(p => p.GetSomeValueAsync()) .ReturnsAsync(1 ) .ThrowsAsync(new Exception());
Mock对象属性 1 2 3 4 var mock = new Mock<Product>();mock.Setup(p => p.Id).Returns("1" ); mock.Setup(p => p.Name).Returns("Bar" ); mock.Setup(p => p.Img).Returns("xxx" );
也可以对属性的Get、Set方法
1 2 mock.SetupGet(p => p.Name) ... ; mock.SetupSet(p => p.Name = It.IsAny<string >()) ... ;
属性链返回值
1 2 var mock = new Mock<HttpContextBase>();mock.SetupGet(p => p.Response.Request.UserAgent).Returns("My Browser" );
Mock检查 1 mock.Setup(p => p.Delete(It.IsAny<string >())).Verifiable();
全部检查
自定义检查失败消息
1 mock.Setup(p => p.Send(It.IsAny<string >())).Verifiable("Send was never invoked" );
自选检查mock
1 Mock.Verify(mock, anotherMock, yetAnotherMock);
Mock仓库 1 var mockRepository = new MockRepository(MockBehavior.Strict) { DefaultValue = DefaultValue.Mock };
1 2 var _repository = repository.Create<IProductRepository>();var _service = repository.Create<IProductService>(MockBehavior.Loose);
Mock深层对象 有时候想直接获得深层对象的mock,Mock.Get
对象属性这种方法非常有用
1 var mock = Mock.Get(logger);
1 2 3 4 var mock = new Mock<HttpContextBase>();var context = mock.Object;var request = Mock.Get(context.Request);
Mock自定义 默认空
1 var mock = new Mock<IProductService> { DefaultValue = DefaultValue.Empty };
模拟数据
1 var mock = new Mock<IProductService> { DefaultValue = DefaultValue.Mock };
自定义默认值提供器
1 2 3 public class MyCustomDefaultValueProvider : DefaultValueProvider { ... }var mock = new Mock<IProductService> { DefaultValueProvider = new MyCustomDefaultValueProvider() };