using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Moq; using Moq.Internals; using Moq.Language; using Moq.Protected; using Xunit; using MoqStudy.MockModel; namespace MoqStudy.Test { /// /// Moq官网快速入门示例 学习 /// https://github.com/Moq/moq4/wiki/Quickstart /// public class MoqDemoTest:IDisposable { //说明:因为是个人学习用,所有注释比较多 #region xUnit 初始准备 Mock mock = new Mock(); IFoo ifoo; delegate void SubmitCallback(ref Bar bar); public MoqDemoTest() { ifoo = mock.Object; } #endregion #region 设置方法 /// /// 模拟方法 /// [Fact] public void Method_Test() { mock //参数为"ping"时,DoSomething 返回 true .Setup(foo => foo.DoSomething("ping")) .Returns(true); Assert.True(ifoo.DoSomething("ping")); Assert.False(ifoo.DoSomething("sssss")); } /// /// 输出(out)参数 /// [Fact] public void Method_OutArgument_Test() { //输出参数 var outString = "ack"; mock //TryParse 返回 true,输出参数 outString 被赋值为 "ACK" //调用方法之后,outString保持原值,方法并不会改变输出能数的值 .Setup(foo => foo.TryParse("ping", out outString)) .Returns(true); Assert.True(mock.Object.TryParse("ping", out outString)); Assert.Equal("ack", outString); //调用方法,并不会改变输出参数的值,即便是方法不符合设置。 Assert.False(mock.Object.TryParse("xyz", out outString)); Assert.Equal("ack", outString); } /// /// 引用(ref)参数 /// [Fact] public void Method_RefArgument_Test() { var instance = new Bar(); mock //只有当调用方法的ref参数与设置时ref参数是相同实例时才匹配。 .Setup(foo => foo.Submit(ref instance)) .Returns(true); //调用参数与设置参数是同一个实例,匹配 Assert.True(ifoo.Submit(ref instance)); //调用方法的ref参数与设置时ref参数,不是同一个实例,不匹配 var notSame = new Bar(); Assert.False(ifoo.Submit(ref notSame)); } /// /// 设置返回值 /// [Fact] public void Method_Return_Test() { mock //设置返回值 .Setup(x => x.DoSomethingStringy(It.IsAny())) .Returns("ack"); Assert.Equal("ack", ifoo.DoSomethingStringy("ABC")); Assert.Equal("ack", ifoo.DoSomethingStringy("123")); Assert.Equal("ack", ifoo.DoSomethingStringy("Abc123_/+")); } /// /// 返回值中使用参数 /// [Fact] public void Method_Return_UseArgument_Test() { mock //在返回值中,使用方法参数 .Setup(x => x.DoSomethingStringy(It.IsAny())) //返回参数的小写形式 .Returns((string s) => s.ToLower()); Assert.Equal("ab", ifoo.DoSomethingStringy("AB")); Assert.Equal("ab", ifoo.DoSomethingStringy("aB")); Assert.Equal("ab", ifoo.DoSomethingStringy("ab")); } /// /// 返回值中使用多参数 /// [Fact] public void Method_Return_UseMultiArgument_Test() { mock //在返回值中,使用多方法参数 .Setup(x => x.DoSomething(It.IsAny(), It.IsAny())) //返回,两参数字符值是否相等 .Returns((int v,string s) => v.ToString() == s.ToLower()); Assert.True(ifoo.DoSomething(11,"11")); Assert.False(ifoo.DoSomething(11,"aa")); Assert.False(ifoo.DoSomething(11,"110")); } /// /// 调用特定参数时抛出异常 /// [Fact] public void Method_Throwing_Test() { mock.Setup(foo => foo.DoSomething("reset")).Throws(); mock.Setup(foo => foo.DoSomething("")).Throws(new ArgumentException("command")); //指定值时,抛出异常 Assert.Throws(() => { ifoo.DoSomething("reset"); }); Assert.Throws(() => { ifoo.DoSomething(""); }); //非指定值时(loose模式时),不抛出异常 Assert.False(ifoo.DoSomething("not reset")); } /// /// lazy evaluating return value /// 设置返回值,延迟求值 /// [Fact] public void Method_Return_Lazy_Test() { var count = 1; mock //返回值设置为踉踪变量,变量值变化,返回值踉着变化 .Setup(foo => foo.GetCount()) //返回值踉踪变量count,count值变化,返回值踉着变化 .Returns(() => count); //变量值变为3,则返回值踉着变为3 count = 3; Assert.Equal(3, ifoo.GetCount()); //变量值变为5,则返回值踉着变为5 count = 5; Assert.Equal(5, ifoo.GetCount()); } /// /// 每次调用方法,返回不同的值 /// 实质为:调用Callback(),改变返回值引用的变量值 /// 另一种方法,见:Miscellaneous_Different_Test()方法 /// [Fact] public void Method_Return_Different_Test() { var calls = 0; mock .Setup(foo => foo.GetCount()) //返回值设置为:踉踪变量 .Returns(() => calls) //调用方法,改变踉踪变量的值 .Callback(() => calls++); Assert.Equal(0, ifoo.GetCount()); Assert.Equal(1, ifoo.GetCount()); Assert.Equal(2, ifoo.GetCount()); Assert.Equal(3, ifoo.GetCount()); Assert.Equal(4, ifoo.GetCount()); } /// /// 重复设置 /// 后面的设置会覆盖前面的设置 /// [Fact] public void Method_RepeatSet_Test() { mock.//首次设置 Setup(f => f.DoSomething("ping")) .Returns(true); Assert.True(ifoo.DoSomething("ping")); mock.//重复设置相同参数 Setup(f => f.DoSomething("ping")) .Returns(false); //后面的设置,覆盖前面的设置 Assert.False(ifoo.DoSomething("ping")); } /// /// 多次设置同一参数不同值的返回值 /// 保持多次设置 /// [Fact] public void Method_MultiSet_Test() { mock.//设置参数为"ping" Setup(f => f.DoSomething("ping")) .Returns(true); mock.//设置参数为"xyz" Setup(f => f.DoSomething("xyz")) .Returns(true); Assert.True(ifoo.DoSomething("ping")); Assert.True(ifoo.DoSomething("xyz")); //其它均为false Assert.False(ifoo.DoSomething("abc1232")); } #endregion #region 匹配参数 /// /// 匹配参数的任意值 /// [Fact] public void Argument_IsAny_Test() { mock //匹配参数的任意值 .Setup(foo => foo.DoSomething(It.IsAny())) .Returns(true); Assert.True(ifoo.DoSomething(null)); Assert.True(ifoo.DoSomething("")); Assert.True(ifoo.DoSomething("ping")); Assert.True(ifoo.DoSomething("ABCde123456")); Assert.True(ifoo.DoSomething(" +=-$%)()")); } /// /// 匹配"ref"的任意值(Moq需要4.8及更高版本) /// [Fact] public void Argument_Ref_IsAny_Test() { mock .Setup(foo => foo.Submit(ref It.Ref.IsAny)) .Returns(true); Bar bar1 = null; Bar bar2 = new Bar(); Bar bar3 = new Bar() { Baz = null }; Bar bar4 = new Bar() { Baz = new Baz() { Name="wanggaofeng"} }; Assert.True(ifoo.Submit(ref bar1)); Assert.True(ifoo.Submit(ref bar2)); Assert.True(ifoo.Submit(ref bar3)); Assert.True(ifoo.Submit(ref bar4)); } /// /// 匹配符合Func<>的任意值 /// [Fact] public void Argument_Is_Test() { mock //参数匹配 Fun<>() .Setup(foo => foo.Add(It.Is(i => i % 2 == 0))) .Returns(true); Assert.True(ifoo.Add(2)); Assert.True(ifoo.Add(4)); Assert.True(ifoo.Add(6)); Assert.False(ifoo.Add(1)); Assert.False(ifoo.Add(3)); Assert.False(ifoo.Add(5)); } /// /// 匹配指定范围 /// [Fact] public void Argument_Range_Test() { mock //参数匹配指定范围,注意边界值 .Setup(foo => foo.Add ( //Range.Inclusive 排除边界值 //Range.Exclusive 包括边界值 It.IsInRange(0, 10, Range.Inclusive)) ) .Returns(true); Assert.True(ifoo.Add(0)); Assert.True(ifoo.Add(5)); Assert.True(ifoo.Add(10)); Assert.False(ifoo.Add(-1)); Assert.False(ifoo.Add(11)); Assert.False(ifoo.Add(10000)); } /// /// 匹配正则表达式 /// [Fact] public void Agument_Regex_Test() { mock //参数匹配指定正则表达式 .Setup(x => x.DoSomethingStringy ( //指定正则及正则选项 It.IsRegex("[a-d]+", System.Text.RegularExpressions.RegexOptions.IgnoreCase)) ) .Returns("foo"); Assert.Equal("foo", ifoo.DoSomethingStringy("abcdaaacccddddd")); Assert.NotEqual("foo", ifoo.DoSomethingStringy("123456")); } #endregion #region 设置属性 /// /// 设置属性 /// [Fact] public void Property_Setup_Test() { mock //设置属性值 .Setup(f => f.Name) .Returns("wanggaofeng"); Assert.Equal("wanggaofeng", ifoo.Name); } /// /// 自动模拟多层属性 /// (递归模拟属性) /// [Fact] public void Property_Setup_Recursive_Test() { mock //自动模拟多层(即递归Mocks) .Setup(f => f.Bar.Baz.Name) .Returns("Bar.Baz.Name"); Assert.Equal("Bar.Baz.Name", ifoo.Bar.Baz.Name); } /// /// 验证属性设置 /// [Fact] public void Property_VerifySet_Test() { MockException verifyException = null; //不执行,为属性赋指定的值,则验证出现异常 try { //检查代码 foo.Name = "foo" 已经执行过 mock.VerifySet(foo => foo.Name = "foo"); verifyException = null; } catch (MockException ex) { verifyException = ex; } //验证发生异常 Assert.NotNull(verifyException); //为属性赋指定的值,验证通过 try { mock //设置属性(loose模式下,可以不设置) .Setup(f => f.Name) .Returns("bbb"); //执行赋值语句 ifoo.Name = "foo"; //检查代码 foo.Name = "foo" 已经执行过 mock.VerifySet(foo => foo.Name = "foo"); verifyException = null; } catch (MockException ex) { verifyException = ex; } Assert.Equal("bbb", ifoo.Name); Assert.Null(verifyException); } /// /// 期望(等同于检查),属性已经被设置为指定的值 /// 用法暂不明确:可以用VerifySet替代本功能 /// [Fact] public void Property_SetupSet_Test() { mock //todo:用法不明确,待查 //可以使用 VerifySet 替代 .SetupSet(foo => foo.Name = "foo"); Assert.Equal(1,2); } /// /// 验证设置属性 /// 用法暂不明确:可以用VerifySet替代本功能 /// [Fact] public void Property_SetupGet_Test() { mock //todo:用法不明确,待查 .SetupGet(foo => foo.Name); Assert.True(1==2); } /// /// 设置后,改变属性值,再次调用时,属性值不变 /// [Fact] public void Property_Setup_Change_Test() { mock.Setup(f => f.Name) .Returns("ping"); Assert.Equal("ping", ifoo.Name); //为属性赋新值 ifoo.Name = "ack"; //再次调用,属性值保持"ping"不变 Assert.False(ifoo.Name == "ack"); Assert.True(ifoo.Name == "ping"); } /// /// 重复设置属性 /// 后面设置的属性值,覆盖前面的设置 /// [Fact] public void Property_Repeat_Setup_Test() { mock //设置属性值为 ping .Setup(f => f.Name) .Returns("ping"); Assert.Equal("ping", ifoo.Name); mock //再次设置属性值为 ack .Setup(f => f.Name) .Returns("ack"); Assert.Equal("ack", ifoo.Name); } /// /// 存根设置的属性值 /// 设置后自动开始跟踪属性值,为属性赋值后,调用属性值踉着改变 /// [Fact] public void Property_SetupProperty_Test() { //注意对比Setup设置:Setup设置的属性,为属性赋新值后,调用属性值保持不变 // 设置并开始“跟踪”此属性 mock.SetupProperty(f => f.Name); // 或者 设置默认值并开始“跟踪”此属性 mock.SetupProperty(f => f.Name, "foo"); // 初始值被存储 Assert.Equal("foo", ifoo.Name); // 设定新值改,初始值被新值覆盖 ifoo.Name = "bar"; Assert.Equal("bar", ifoo.Name); } /// /// 存根所有模拟属性 /// [Fact] public void Property_SetupAllProperties_Test() { mock //存根所有属性,这意味着设置其值将导致它被保存并在请求属性时返回。 //每个属性值设置为Mock框架的默认值。 .SetupAllProperties(); Assert.Null(ifoo.Name); //赋新值 ifoo.Name = "change"; Assert.Equal("change", ifoo.Name); } #endregion #region 设置事件 [Fact] public void Events_Test() { //要单独写测试 } #endregion #region 回调函数 /// /// 设置回调函数 /// [Fact] public void Callback_Test() { var calls = 0; mock //回调函数,返回后,执行其它代码 .Setup(foo => foo.DoSomething("ping")) .Returns(true) .Callback(() => calls++); //方法执行成功,执行回调 Assert.True(ifoo.DoSomething("ping")); Assert.Equal(1, calls); //方法执行成功,再次执行回调 Assert.True(ifoo.DoSomething("ping")); Assert.Equal(2, calls); //方法执行失败,不执行回调 Assert.False(ifoo.DoSomething("xyz")); Assert.Equal(2, calls); } /// /// 回调函数中使用设置参数 /// [Fact] public void Callback_Argument_Test() { var callArgs = new List(); mock.Setup(foo => foo.DoSomething(It.IsAny())) .Returns(true) .Callback((string s) => callArgs.Add(s)); // 或者如下:回调函数的泛型版本 // .Callback(s => callArgs.Add(s)); Assert.True(ifoo.DoSomething("first")); Assert.Single(callArgs); Assert.Contains("first", callArgs); Assert.True(ifoo.DoSomething("second")); Assert.Equal(2, callArgs.Count); Assert.Contains("second", callArgs); } /// /// 泛型回调函数中使用参数 /// [Fact] public void Callback_Generic_Argument_Test() { var callArgs = new List(); mock.Setup(foo => foo.DoSomething(It.IsAny())) .Returns(true) .Callback(s => callArgs.Add(s)); Assert.True(ifoo.DoSomething("first")); Assert.Single(callArgs); Assert.Contains("first", callArgs); Assert.True(ifoo.DoSomething("second")); Assert.Equal(2, callArgs.Count); Assert.Contains("second", callArgs); } /// /// 回调函数中使用多个参数 /// [Fact] public void Callback_MultiArgument_Test() { var callCount = 0; var callArgs = new List(); mock.Setup(foo => foo.DoSomething(It.IsAny(), It.IsAny())) .Returns(true) .Callback ( (i, s) => { callCount += 1; callArgs.Add(s); } ); Assert.True(ifoo.DoSomething(99, "first")); Assert.Equal(1, callCount); Assert.Contains("first", callArgs); Assert.True(ifoo.DoSomething(88, "second")); Assert.Equal(2, callCount); Assert.Contains("second", callArgs); } /// /// 指定回调函数位置:前置执行或后置执行 /// [Fact] public void Callback_BeforeAndAfter_Test() { var returnBefore = new List(); var returnAfter = new List(); //此验证并不严谨. 控制台程序已经另行验证过。 // 也可以在xUnit中打印信息验证 mock.Setup(foo => foo.DoSomething(It.IsAny())) .Callback(s => returnBefore.Add(s)) .Returns(true) .Callback(s => returnAfter.Add(s)); Assert.True(ifoo.DoSomething("xyz")); Assert.Contains("xyz", returnBefore); Assert.Contains("xyz", returnAfter); } /// /// 回调函数中使用 re/out 参数 /// [Fact] public void Callback_RefAndOut_Parameter_Test() { mock //回调函数中使用 re/out 参数 .Setup(foo => foo.Submit(ref It.Ref.IsAny)) .Callback ( new SubmitCallback ( (ref Bar bar) => { bar.Baz.Name = "xyz"; } ) ); var myBar = new Bar() { Baz = new Baz() { Name = "gaofeng" } }; mock.Object.Submit(ref myBar); Assert.Equal("xyz", myBar.Baz.Name); //和没有回调函数的对比验证 var mock2 = new Mock(); mock2.Setup(foo => foo.Submit(ref It.Ref.IsAny)); var myBar2 = new Bar() { Baz = new Baz() { Name = "gaofeng" } }; mock2.Object.Submit(ref myBar2); Assert.Equal("gaofeng", myBar2.Baz.Name); } #endregion #region 验证 /// /// 验证匹配项是否调用 /// 不设置,验证出现异常 /// [Fact] public void Verify_NoSetup_Test() { //验证匹配项是否调用 Assert.Throws(() => mock.Verify(f => f.Add(1))); } /// /// 验证匹配项是否调用 /// 设置后不运行,验证出现异常 /// [Fact] public void Verify_Setup_NoRun_Test() { mock //验证匹配项是否调用 .Setup(f => f.Add(2)) .Returns(true); Assert.Throws(() => { mock.Verify(f => f.Add(2)); }); } /// /// 验证匹配项是否调用 /// 设置并调用后,验证通过 /// [Fact] public void Verify_Setup_Test() { mock //设置 .Setup(f => f.Add(2)) .Returns(true); //调用运行 ifoo.Add(2); //通过验证 mock.Verify(f => f.Add(2)); } /// /// 验证失败时,自定义异常信息 /// [Fact] public void Verify_ErrorMessage_Test() { MockException mockException = null; try { mock.Verify(foo => foo.DoSomething("ping"), "Custom exception message"); mockException = null; } catch (MockException ex) { mockException = ex; } Assert.StartsWith("Custom exception message", mockException.Message); } /// /// 验证方法从未被调用 /// [Fact] public void Verify_Never_Test() { mock.Verify(foo => foo.DoSomething("ping"), Times.Never()); ifoo.DoSomething("ping"); Assert.Throws(()=> { mock.Verify(foo => foo.DoSomething("ping"), Times.Never()); }); } /// /// 验证至少调用过一次 /// [Fact] public void Verify_AtLeastOnce_Test() { var mock = new Mock(); var ifoo = mock.Object; MockException mockException = null; var customInfo = "custom info"; try { mock.Verify(foo => foo.DoSomething("ping"), Times.AtLeastOnce(), customInfo); } catch(MockException ex) { mockException = ex; } Assert.NotNull(mockException); Assert.StartsWith(customInfo, mockException.Message); //验证通过 mock.Setup(f => f.DoSomething("ping")).Returns(true); try { mock.Object.DoSomething("ping"); mock.Verify(foo => foo.DoSomething("ping"), Times.AtLeastOnce(), customInfo); mockException = null; } catch (MockException ex) { mockException = ex; } Assert.Null(mockException); } /// /// 验证属性被读取过 /// [Fact] public void Verify_Propertie_Get_Test() { var mock = new Mock(); var ifoo = mock.Object; MockException mockException = null; var customInfo = "custom info"; try { mock.VerifyGet(foo => foo.Name, customInfo); } catch (MockException ex) { mockException = ex; } Assert.NotNull(mockException); Assert.StartsWith(customInfo, mockException.Message); //验证通过情况 mock.SetupGet(f => f.Name); //或者 //mock.SetupProperty(f => f.Name); try { //读取1次属性 var rr = ifoo.Name; mock.VerifyGet(foo => foo.Name, customInfo); mockException = null; } catch (MockException ex) { mockException = ex; } Assert.Null(mockException); } /// /// 验证对属性的赋值(已弃用的方法) /// [Fact] public void Verify_Propertie_Set_Test() { var mock = new Mock(); var ifoo = mock.Object; MockException mockException = null; var customInfo = "custom info"; try { mock.VerifySet(foo => foo.Name, customInfo); } catch (MockException ex) { mockException = ex; } Assert.NotNull(mockException); Assert.StartsWith(customInfo, mockException.Message); //验证通过情况 mock.SetupSet(f => f.Name); //或者 //mock.SetupProperty(f => f.Name); try { ifoo.Name = "zhangsan"; mock.VerifySet(foo => foo.Name, customInfo); mockException = null; } catch (MockException ex) { mockException = ex; } Assert.Null(mockException); } /// /// 验证对于属性设置特定的值 /// [Fact] public void Verify_PropertieValue_Test() { MockException mockException = null; var customInfo = "custom info"; try { mock.VerifyGet(foo => foo.Name, customInfo); } catch (MockException ex) { mockException = ex; } Assert.NotNull(mockException); Assert.StartsWith(customInfo, mockException.Message); //验证通过情况 mock.SetupProperty(f => f.Name, "wanggaofeng"); try { var name = mock.Object.Name; mock.VerifyGet(foo => foo.Name, customInfo); mockException = null; } catch (MockException ex) { mockException = ex; } Assert.Null(mockException); } /// /// 验证匹配的参数 /// [Fact] public void Verify_Propertie_Argument_Test() { MockException mockException = null; var customInfo = "custom info"; try { mock.VerifySet(foo => foo.Value = It.IsInRange(1, 5, Range.Inclusive), customInfo); } catch (MockException ex) { mockException = ex; } Assert.NotNull(mockException); Assert.StartsWith(customInfo, mockException.Message); //验证通过情况 mock.SetupProperty(f => f.Value, 3); try { //赋值一次 mock.Object.Value = 4; mock.VerifySet(foo => foo.Value = It.IsInRange(1, 5, Range.Inclusive), customInfo); mockException = null; } catch (MockException ex) { mockException = ex; } Assert.Null(mockException); } #endregion #region 定制模拟行为 /// /// Customizing Mock Behavior /// 定制Mock模式 /// 默认模式,等同宽松模式:未设置的Mock行为或属性保持默认值或默认行为 /// 严格模式:未设置的Mock行为或属性,不可使用(异常) /// [Fact] public void CustomMock_Behavior_Test() { //默认模式,等同宽松模式 var looseOrDefaultMock = new Mock(MockBehavior.Loose); Assert.Null(looseOrDefaultMock.Object.Name); Assert.False(looseOrDefaultMock.Object.Add(1)); //严格模式 var strictMock = new Mock(MockBehavior.Strict); Assert.Throws(()=> { var temp = strictMock.Object.Name; }); Assert.Throws(()=> { strictMock.Object.Add(1); }); } /// /// Callbase /// 使用Callbase /// 如果没有重写基类的实现,默认将不会调用基类,在 Mock Web/Html 控件的是必须的 /// http://www.360doc.com/content/14/0729/09/10504424_397815601.shtml /// [Fact] public void CustomMock_Callbase_Test() { /* Invoke base class implementation if no expectation overrides the member (a.k.a. "Partial Mocks" in Rhino Mocks): default is false. (this is required if you are mocking Web/Html controls in System.Web!) */ //如果没有重写基类的实现,默认将不会调用基类,在 Mock Web/Html 控件的是必须的。 var mock = new Mock { CallBase = true }; //todo:需要验证 } /// /// 创造自动递归的Mock, Mock 对象对于它的任何成员将会返回一个新的 Mock 对象。 /// [Fact] public void CustomMock_Automatic_Test() { var mock = new Mock { DefaultValue = DefaultValue.Mock }; // default is DefaultValue.Empty //默认是 DefaultValue.Empty // this property access would return a new mock of Bar as it's "mock-able" // 现在这个属性将会返回一个新的 Mock 对象 Bar value = mock.Object.Bar; Assert.NotNull(value); // the returned mock is reused, so further accesses to the property return the same mock instance. this allows us to also use this instance to set further expectations on it if we want // 可以使用返回的 Mock 对象, 后即对属性的访问返回相同的对象实例,这就允许我们可以进行后继的设置 //再次转为Mock类型 var barMock = Mock.Get(value); barMock.Setup(b => b.Submit()).Returns(true); } /// /// 中心化的 Mock 实例创建和管理:你可以在一个地方使用 MockRepository 创建和验证所有的 Mock 对象,设置 MockBehavior, CallBse 和 DefaultValue 约束。 /// [Fact] public void CustomMock_MockRepository_Test() { var repository = new MockRepository(MockBehavior.Strict) { DefaultValue = DefaultValue.Mock }; // Create a mock using the repository settings var fooMock = repository.Create(); // Create a mock overriding the repository settings var barMock = repository.Create(MockBehavior.Loose); // Verify all verifiable expectations on all mocks created through the repository try { repository.Verify(); Assert.True(true); } catch(Exception ex) { Assert.Null(ex); } } #endregion #region 其它 /// /// Miscellaneous /// 按调用顺序返回不同的值,超过次数报异常 /// [Fact] public void Miscellaneous_Different_Test() { var mock = new Mock(); var ifoo = mock.Object; mock.SetupSequence(f => f.GetCount()) .Returns(3) // 第1次调用,返回3 .Returns(2) // 第2次调用,返回2 .Returns(1) // 第3次调用,返回1 .Returns(0) // 第4次调用,返回0 .Throws(new InvalidOperationException()); // 第5及后续调用,异常 Assert.Equal(3, ifoo.GetCount()); Assert.Equal(2, ifoo.GetCount()); Assert.Equal(1, ifoo.GetCount()); Assert.Equal(0, ifoo.GetCount()); Assert.Throws(() => ifoo.GetCount()); } /// /// 设置受保护成员的期望值 /// (无法获得这些成员的智能感知,因此使用成员名作为字符串访问它们) /// [Fact] public void Miscellaneous_Protected_Test() { //todo:用法还不清楚 var mock = new Mock(); mock.Protected() .Setup("GetAge",2) .Returns(5); //todo:设置好了,怎么使用是个问题 // if you need argument matching, // you MUST use ItExpr rather than It planning on improving this for vNext // (see below for an alternative in Moq 4.8) mock.Protected() .Setup("Execute", ItExpr.IsAny()) .Returns(true); } #endregion #region 高级特性 [Fact] public void Advanced_GetFromMocked_Test() { // get mock from a mocked instance IFoo foo =new Mock().Object; // get mock instance somehow 以某种方式获取模拟实例 var fooMock = Mock.Get(foo); fooMock.Setup(f => f.GetCount()).Returns(42); Assert.Equal(42, fooMock.Object.GetCount()); } /// /// implementing multiple interfaces in mock /// 在模拟中实现多个接口 /// [Fact] public void Advanced_MulttipleInteface_Test() { var mock = new Mock(); var disposableFoo = mock.As(); // now the IFoo mock also implements IDisposable // 现在,IFoo 模拟也实现了IDisposable接口 disposableFoo.Setup(disposable => disposable.Dispose()); var ifoo = disposableFoo.Object; ifoo.Dispose(); //todo:设计测试方法 } /// /// implementing multiple interfaces in single mock /// 在单个模拟中实现多个接口 /// [Fact] public void Advanced_MulttipleInteface_Single_Test() { var mock = new Mock(); mock.Setup(foo => foo.Name).Returns("Fred"); mock.As().Setup(disposable => disposable.Dispose()); } /// /// 自定义匹配方法 /// [Fact] public void Advanced_CustomMatcher_Test() { //var mock = new Mock(); //var ifoo = mock.Object; //mock.Setup(foo => foo.DoSomething(IsLarge())) // .Throws(); //Assert.False(ifoo.DoSomething("xyz")); //Assert.Throws(()=> { ifoo.DoSomething("a1234567890"); }); } #endregion #region LINQ to Mocks #endregion #region xUnit 清理 public void Dispose() { } #endregion } }