From d2b37932bba93c53f3b34e1ae67b5918014bf41d Mon Sep 17 00:00:00 2001 From: bicijinlian Date: Sun, 29 Jul 2018 23:23:55 +0800 Subject: [PATCH] MoqDemo update --- MoqStudy.Test/MoqDemoTest.cs | 375 +++++++++++++++++++++++------------ 1 file changed, 247 insertions(+), 128 deletions(-) diff --git a/MoqStudy.Test/MoqDemoTest.cs b/MoqStudy.Test/MoqDemoTest.cs index f425bbb..34517f2 100644 --- a/MoqStudy.Test/MoqDemoTest.cs +++ b/MoqStudy.Test/MoqDemoTest.cs @@ -21,9 +21,12 @@ namespace MoqStudy.Test /// public class MoqDemoTest:IDisposable { + //说明:因为是个人学习用,所有注释比较多 + #region xUnit 初始准备 Mock mock = new Mock(); IFoo ifoo; + delegate void SubmitCallback(ref Bar bar); public MoqDemoTest() { ifoo = mock.Object; @@ -196,7 +199,47 @@ namespace MoqStudy.Test 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 匹配参数 @@ -312,97 +355,180 @@ namespace MoqStudy.Test /// 设置属性 /// [Fact] - public void Properties_Setup_Test() + public void Property_Setup_Test() { - var mock = new Mock(); - var ifoo = mock.Object; + mock //设置属性值 + .Setup(f => f.Name) + .Returns("wanggaofeng"); - //设置属性值 - 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"); - // auto-mocking hierarchies (a.k.a. recursive mocks) - // 自动模拟多层(即递归Mocks) - mock.Setup(f => f.Bar.Baz.Name).Returns("Bar.Baz.Name"); Assert.Equal("Bar.Baz.Name", ifoo.Bar.Baz.Name); } /// - /// 设置属性:todo SetupSet用法 + /// 验证属性设置 /// [Fact] - public void Properties_SetupSet_Test() + public void Property_VerifySet_Test() { - var mock = new Mock(); - var ifoo = mock.Object; + MockException verifyException = null; - //expects an invocation to set the value to "foo" - //期望将调用值设置为“foo” - var cc= mock.SetupSet(foo => foo.Name = "foo"); - Assert.Equal("foo", ifoo.Name); + //不执行,为属性赋指定的值,则验证出现异常 + 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 Properties_SetupGet_Test() + public void Property_SetupGet_Test() { - var mock = new Mock(); - var ifoo = mock.Object; + mock //todo:用法不明确,待查 + .SetupGet(foo => foo.Name); - // verify the setter directly - mock.SetupGet(foo => foo.Name); - ifoo.Name = "foo"; - Assert.Equal("foo", ifoo.Name); + Assert.True(1==2); } /// - /// Setup a property so that it will automatically start tracking its value (also known as Stub) - /// 设置属性以使其自动开始跟踪其值(也称为存根) + /// 设置后,改变属性值,再次调用时,属性值不变 /// [Fact] - public void Properties_SetupProperty_Test() + public void Property_Setup_Change_Test() { - var mock = new Mock(); + mock.Setup(f => f.Name) + .Returns("ping"); - // start "tracking" sets/gets to this property - // 开始“跟踪”设置/获取此属性 - mock.SetupProperty(f => f.Name); + Assert.Equal("ping", ifoo.Name); - // alternatively, provide a default value for the stubbed property - // 或者,为插入属性提供默认值。 - mock.SetupProperty(f => f.Name, "foo"); + //为属性赋新值 + ifoo.Name = "ack"; + //再次调用,属性值保持"ping"不变 + Assert.False(ifoo.Name == "ack"); + Assert.True(ifoo.Name == "ping"); + } - // Now you can do: - // 现在你可以这么做: + /// + /// 重复设置属性 + /// 后面设置的属性值,覆盖前面的设置 + /// + [Fact] + public void Property_Repeat_Setup_Test() + { + mock //设置属性值为 ping + .Setup(f => f.Name) + .Returns("ping"); - IFoo foo = mock.Object; - // Initial value was stored - // 初始值被存储 - Assert.Equal("foo", foo.Name); + Assert.Equal("ping", ifoo.Name); - // New value set which changes the initial value - // 新的设定值改变初始值 - foo.Name = "bar"; - Assert.Equal("bar", foo.Name); + mock //再次设置属性值为 ack + .Setup(f => f.Name) + .Returns("ack"); + + Assert.Equal("ack", ifoo.Name); } /// - /// Stub all properties on a mock (not available on Silverlight) - /// 存根所有模拟属性 + /// 存根设置的属性值 + /// 设置后自动开始跟踪属性值,为属性赋值后,调用属性值踉着改变 /// [Fact] - public void Properties_SetupAllProperties_Test() + public void Property_SetupProperty_Test() { - var mock = new Mock(); - var ifoo = mock.Object; - mock.SetupAllProperties(); + //注意对比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); } @@ -425,11 +551,10 @@ namespace MoqStudy.Test [Fact] public void Callback_Test() { - var mock = new Mock(); - var ifoo = mock.Object; var calls = 0; - mock.Setup(foo => foo.DoSomething("ping")) + mock //回调函数,返回后,执行其它代码 + .Setup(foo => foo.DoSomething("ping")) .Returns(true) .Callback(() => calls++); @@ -447,20 +572,17 @@ namespace MoqStudy.Test } /// - /// access invocation arguments - /// 回调函数中使用参数 + /// 回调函数中使用设置参数 /// [Fact] public void Callback_Argument_Test() { - var mock = new Mock(); - var ifoo = mock.Object; 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")); @@ -474,16 +596,13 @@ namespace MoqStudy.Test } /// - /// alternate equivalent generic method syntax /// 泛型回调函数中使用参数 /// [Fact] public void Callback_Generic_Argument_Test() { - var mock = new Mock(); - var ifoo = mock.Object; var callArgs = new List(); - //问题:多次设置,会什么结果 + mock.Setup(foo => foo.DoSomething(It.IsAny())) .Returns(true) .Callback(s => callArgs.Add(s)); @@ -499,15 +618,11 @@ namespace MoqStudy.Test } /// - /// access arguments for methods with multiple parameters /// 回调函数中使用多个参数 /// [Fact] public void Callback_MultiArgument_Test() { - var mock = new Mock(); - var ifoo = mock.Object; - var callCount = 0; var callArgs = new List(); @@ -532,15 +647,11 @@ namespace MoqStudy.Test } /// - /// callbacks can be specified before and after invocation /// 指定回调函数位置:前置执行或后置执行 /// [Fact] public void Callback_BeforeAndAfter_Test() { - var mock = new Mock(); - var ifoo = mock.Object; - var returnBefore = new List(); var returnAfter = new List(); @@ -558,110 +669,125 @@ namespace MoqStudy.Test } /// - /// callbacks for methods with `ref` / `out` parameters are possible but require some work (and Moq 4.8 or later) /// 回调函数中使用 re/out 参数 /// [Fact] public void Callback_RefAndOut_Parameter_Test() { - //var mock = new Mock(); - //mock.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); + 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" } + }; - //Assert.Equal("xyz", myBar.Baz.Name); + mock.Object.Submit(ref myBar); - ////对比验证 - //var mock2 = new Mock(); - //mock2.Setup(foo => foo.Submit(ref It.Ref.IsAny)); - //var myBar2 = new Bar() - //{ - // Baz = new Baz() { Name = "gaofeng" } - //}; + Assert.Equal("xyz", myBar.Baz.Name); - //mock2.Object.Submit(ref myBar2); + //和没有回调函数的对比验证 + var mock2 = new Mock(); + mock2.Setup(foo => foo.Submit(ref It.Ref.IsAny)); + var myBar2 = new Bar() + { + Baz = new Baz() { Name = "gaofeng" } + }; - //Assert.Equal("gaofeng", myBar2.Baz.Name); + mock2.Object.Submit(ref myBar2); + Assert.Equal("gaofeng", myBar2.Baz.Name); } #endregion #region 验证 /// - /// 不设置,进行验证,出现异常 + /// 验证匹配项是否调用 + /// 不设置,验证出现异常 /// [Fact] public void Verify_NoSetup_Test() { - var mock = new Mock(); + //验证匹配项是否调用 Assert.Throws(() => mock.Verify(f => f.Add(1))); } /// - /// 设置后,不运行方法进行验证 + /// 验证匹配项是否调用 + /// 设置后不运行,验证出现异常 /// [Fact] - public void Verify_NoRun_Test() + public void Verify_Setup_NoRun_Test() { - var mock = new Mock(); - var ifoo = mock.Object; - mock.Setup(f => f.Add(2)).Returns(true); + mock //验证匹配项是否调用 + .Setup(f => f.Add(2)) + .Returns(true); Assert.Throws(() => { mock.Verify(f => f.Add(2)); }); } + /// + /// 验证匹配项是否调用 + /// 设置并调用后,验证通过 + /// [Fact] - public void Verify_Test() + public void Verify_Setup_Test() { - var mock = new Mock(); - var ifoo = mock.Object; + mock //设置 + .Setup(f => f.Add(2)) + .Returns(true); - mock.Setup(f => f.Add(2)).Returns(true); + //调用运行 ifoo.Add(2); + + //通过验证 mock.Verify(f => f.Add(2)); } /// - /// Verify with custom error message for failure - /// 在验证失败的时候,提供自定义的错误提示信息 + /// 验证失败时,自定义异常信息 /// [Fact] public void Verify_ErrorMessage_Test() { - var mock = new Mock(); - var ifoo = mock.Object; - Assert.Throws(() => { mock.Verify(foo => foo.DoSomething("ping"), "When doing operation X, the service should be pinged always"); }); + 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); } /// - /// Method should never be called - /// 方法从未被调用 + /// 验证方法从未被调用 /// [Fact] public void Verify_Never_Test() { - var mock = new Mock(); - var ifoo = mock.Object; - mock.Verify(foo => foo.DoSomething("ping"), Times.Never()); + + ifoo.DoSomething("ping"); + Assert.Throws(()=> { + mock.Verify(foo => foo.DoSomething("ping"), Times.Never()); + }); } /// - /// Called at least once - /// 至少调用过一次 + /// 验证至少调用过一次 /// [Fact] public void Verify_AtLeastOnce_Test() @@ -701,7 +827,6 @@ namespace MoqStudy.Test } /// - /// Verify getter invocation, regardless of value /// 验证属性被读取过 /// [Fact] @@ -744,7 +869,6 @@ namespace MoqStudy.Test } /// - /// Verify setter invocation, regardless of value. /// 验证对属性的赋值(已弃用的方法) /// [Fact] @@ -787,14 +911,11 @@ namespace MoqStudy.Test } /// - /// Verify setter invocation, regardless of value /// 验证对于属性设置特定的值 /// [Fact] public void Verify_PropertieValue_Test() { - var mock = new Mock(); - var ifoo = mock.Object; MockException mockException = null; var customInfo = "custom info"; try @@ -826,14 +947,11 @@ namespace MoqStudy.Test } /// - /// Verify setter with an argument matcher /// 验证匹配的参数 /// [Fact] public void Verify_Propertie_Argument_Test() { - var mock = new Mock(); - var ifoo = mock.Object; MockException mockException = null; var customInfo = "custom info"; try @@ -894,6 +1012,7 @@ namespace MoqStudy.Test /// Callbase /// 使用Callbase /// 如果没有重写基类的实现,默认将不会调用基类,在 Mock Web/Html 控件的是必须的 + /// http://www.360doc.com/content/14/0729/09/10504424_397815601.shtml /// [Fact] public void CustomMock_Callbase_Test()