moq 的常用使用方法(推薦)
Moq,就是Mock you。讀音可以讀成Mock~you。是Mock框架的一種。用于測試中的Mock測試。Mock是模擬的意思。Mock是模擬對(duì)象的一種技術(shù)。
測試方法
// 準(zhǔn)備 Mock IFoo 接口 var mock = new Mock<IFoo>(); // 配置準(zhǔn)備模擬的方法,當(dāng)調(diào)用接口中的 DoSomething 方法,并傳遞參數(shù) "bing" 的時(shí)候,返回 true mock.Setup(foo => foo.DoSomething("ping")).Returns(true); // 方法的參數(shù)中使用了 out 參數(shù) // out arguments var outString = "ack"; // 當(dāng)調(diào)用 TryParse 方法的時(shí)候,out 參數(shù)返回 "ack", 方法返回 true, lazy evaluated mock.Setup(foo => foo.TryParse("ping", out outString)).Returns(true); // ref 參數(shù) var instance = new Bar(); // 僅僅在使用 ref 調(diào)用的時(shí)候,才會(huì)匹配下面的測試 mock.Setup(foo => foo.Submit(ref instance)).Returns(true); // 當(dāng)方法返回值得時(shí)候,還可以訪問返回的值 // 這里可以使用多個(gè)參數(shù) mock.Setup(x => x.DoSomething(It.IsAny<string>())) .Returns((string s) => s.ToLower()); // 在被調(diào)用的時(shí)候拋出異常 mock.Setup(foo => foo.DoSomething("reset")).Throws<InvalidOperationException>(); mock.Setup(foo => foo.DoSomething("")).Throws(new ArgumentException("command"); // 延遲計(jì)算返回的結(jié)果 mock.Setup(foo => foo.GetCount()).Returns(() => count); // 在每一次調(diào)用的時(shí)候,返回不同的值 var mock = new Mock<IFoo>(); var calls = 0; mock.Setup(foo => foo.GetCountThing()) .Returns(() => calls) .Callback(() => calls++); // 第一次調(diào)用返回 0, 下一次是 1, 依次類推 Console.WriteLine(mock.Object.GetCountThing());
匹配參數(shù)
// 任意值 mock.Setup(foo => foo.DoSomething(It.IsAny<string>())).Returns(true); // 提供的值必須匹配一個(gè)函數(shù), lazy evaluated mock.Setup(foo => foo.Add(It.Is<int>(i => i % 2 == 0))).Returns(true); // 匹配一個(gè)范圍 mock.Setup(foo => foo.Add(It.IsInRange<int>(0, 10, Range.Inclusive))).Returns(true); // 匹配正則表達(dá)式 mock.Setup(x => x.DoSomething(It.IsRegex("[a-d]+", RegexOptions.IgnoreCase))).Returns("foo");
屬性
// 普通屬性 mock.Setup(foo => foo.Name).Returns("bar"); // 多層的屬性 mock.Setup(foo => foo.Bar.Baz.Name).Returns("baz"); // 期望設(shè)置屬性的值為 "foo" mock.SetupSet(foo => foo.Name = "foo"); // 或者直接驗(yàn)證賦值 mock.VerifySet(foo => foo.Name = "foo");
設(shè)置屬性,以便自動(dòng)跟蹤它的值
// 開始 "tracking" 屬性的 sets/gets mock.SetupProperty(f => f.Name); // 提供一個(gè)默認(rèn)的值 mock.SetupProperty(f => f.Name, "foo"); // 現(xiàn)在,你可以: IFoo foo = mock.Object; // 保存的值 Assert.Equal("foo", foo.Name); // 重新設(shè)置一個(gè)值 foo.Name = "bar"; Assert.Equal("bar", foo.Name);
還可以準(zhǔn)備所有的屬性
mock.SetupAllProperties();
事件
// 拋出一個(gè)事件 mock.Raise(m => m.FooEvent += null, new FooEventArgs(fooValue)); // 多層的后代中的事件 mock.Raise(m => m.Child.First.FooEvent += null, new FooEventArgs(fooValue)); // 當(dāng) Submit 方法被調(diào)用的時(shí)候,拋出一個(gè)事件 mock.Setup(foo => foo.Submit()).Raises(f => f.Sent += null, EventArgs.Empty); // 拋出異常將會(huì)觸發(fā)對(duì)象底層的行為 // 你可能需要在后面進(jìn)行斷言處理 // 拋出一個(gè)自定義的事件 public delegate void MyEventHandler(int i, bool b); public interface IFoo { event MyEventHandler MyEvent; } var mock = new Mock<IFoo>(); ... // 傳遞自定義的事件參數(shù) mock.Raise(foo => foo.MyEvent += null, 25, true);
回調(diào)
var mock = new Mock<IFoo>(); mock.Setup(foo => foo.Execute("ping")) .Returns(true) .Callback(() => calls++); // 使用調(diào)用的參數(shù) mock.Setup(foo => foo.Execute(It.IsAny<string>())) .Returns(true) .Callback((string s) => calls.Add(s)); // 使用泛型語法 mock.Setup(foo => foo.Execute(It.IsAny<string>())) .Returns(true) .Callback<string>(s => calls.Add(s)); // 使用多個(gè)參數(shù) mock.Setup(foo => foo.Execute(It.IsAny<int>(), It.IsAny<string>())) .Returns(true) .Callback<int, string>((i, s) => calls.Add(s)); // 調(diào)用之前和之后的回調(diào) mock.Setup(foo => foo.Execute("ping")) .Callback(() => Console.WriteLine("Before returns")) .Returns(true) .Callback(() => Console.WriteLine("After returns"));
驗(yàn)證
mock.Verify(foo => foo.Execute("ping")); // 在驗(yàn)證失敗的時(shí)候,提供自定義的錯(cuò)誤提示信息 mock.Verify(foo => foo.Execute("ping"), "When doing operation X, the service should be pinged always"); // 從沒有被調(diào)用的方法 mock.Verify(foo => foo.Execute("ping"), Times.Never()); // 至少調(diào)用過一次 mock.Verify(foo => foo.Execute("ping"), Times.AtLeastOnce()); mock.VerifyGet(foo => foo.Name); // 驗(yàn)證對(duì)屬性的賦值. mock.VerifySet(foo => foo.Name); // 驗(yàn)證對(duì)于屬性設(shè)置特定的值 mock.VerifySet(foo => foo.Name ="foo"); // 驗(yàn)證匹配的參數(shù) mock.VerifySet(foo => foo.Value = It.IsInRange(1, 5, Range.Inclusive));
自定義 Mock 行為
Mock 的行為分為嚴(yán)格的 Strict 和寬松的 Loose, 默認(rèn)為寬松的。在嚴(yán)格模式下,使用任何沒有被指定的行為,都將會(huì)拋出異常,寬松模式下,不會(huì)拋出任何異常,方法將會(huì)返回默認(rèn)值或者空的數(shù)組等等。
var mock = new Mock<IFoo>(MockBehavior.Strict);
如果沒有重寫基類的實(shí)現(xiàn),默認(rèn)將不會(huì)調(diào)用基類,在 Mock Web/Html 控件的是必須的。
var mock = new Mock<IFoo> { CallBase = true };
創(chuàng)造自動(dòng)遞歸的 Mock, Mock 對(duì)象對(duì)于它的任何成員將會(huì)返回一個(gè)新的 Mock 對(duì)象。
var mock = new Mock<IFoo> { DefaultValue = DefaultValue.Mock }; // 默認(rèn)是 DefaultValue.Empty // 現(xiàn)在這個(gè)屬性將會(huì)返回一個(gè)新的 Mock 對(duì)象 IBar value = mock.Object.Bar; // 可以使用返回的 Mock 對(duì)象, 后即對(duì)屬性的訪問返回相同的對(duì)象實(shí)例 // 這就允許我們可以進(jìn)行后繼的設(shè)置 // set further expectations on it if we want var barMock = Mock.Get(value); barMock.Setup(b => b.Submit()).Returns(true);
中心化的 Mock 實(shí)例創(chuàng)建和管理:你可以在一個(gè)地方使用 MockRepository 創(chuàng)建和驗(yàn)證所有的 Mock 對(duì)象,設(shè)置 MockBehavior, CallBse 和 DefaultValue 約束。
var factory = new MockFactory(MockBehavior.Strict) { DefaultValue = DefaultValue.Mock }; // 創(chuàng)建 Mock 對(duì)象 var fooMock = factory.Create<IFoo>(); // 在創(chuàng)建的時(shí)候重寫倉庫的設(shè)置 var barMock = factory.Create<IBar>(MockBehavior.Loose); // 驗(yàn)證通過倉庫創(chuàng)建的對(duì)象 factory.Verify();
其它
// 用在測試用例的開始 using Moq.Protected() // 測試中 var mock = new Mock<CommandBase>(); mock.Protected() .Setup<int>("Execute") .Returns(5); // 如果用到了參數(shù)匹配, 必須使用 ItExpr 來代替 It // 以后計(jì)劃改進(jìn) mock.Protected() .Setup<string>("Execute", ItExpr.IsAny<string>()) .Returns(true);
高級(jí)特性
// 從 Mock 實(shí)例重新獲得 Mock 對(duì)象 IFoo foo = // get mock instance somehow var fooMock = Mock.Get(foo); fooMock.Setup(f => f.Submit()).Returns(true); // 實(shí)現(xiàn)多個(gè)接口 var foo = new Mock<IFoo>(); var disposableFoo = foo.As<IDisposable>(); // 現(xiàn)在 IFoo mock 已經(jīng)實(shí)現(xiàn)了接口 IDisposable :) disposableFoo.Setup(df => df.Dispose()); // 定制匹配 mock.Setup(foo => foo.Submit(IsLarge())).Throws<ArgumentException>(); ... public string IsLarge() { return Match<string>.Create(s => !String.IsNullOrEmpty(s) && s.Length > 100); }
以上所述是小編給大家介紹的moq 的常用使用方法,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
相關(guān)文章
Path類與Directory類與File類對(duì)路徑/目錄/文件的操作實(shí)例
本文將詳細(xì)介紹下:Path對(duì)路徑字符串進(jìn)行操作/Directory和DirectoryInfo 對(duì)目錄進(jìn)行操作/File和FileInfo對(duì)文件進(jìn)行操作,感興趣的你可不要錯(cuò)過了哈2013-02-02.Net?Core3.0?WebApi?項(xiàng)目框架搭建之使用Serilog替換掉Log4j
Serilog 是一個(gè)用于.NET應(yīng)用程序的日志記錄開源庫,配置簡單,接口干凈,并可運(yùn)行在最新的.NET平臺(tái)上,這篇文章主要介紹了.Net?Core3.0?WebApi?項(xiàng)目框架搭建之使用Serilog替換掉Log4j,需要的朋友可以參考下2022-02-02.NET?Core部署為Windows服務(wù)的詳細(xì)步驟
這篇文章主要介紹了.NET?Core部署為Windows服務(wù),想要將.NET?Core部署為window服務(wù),項(xiàng)目中需要進(jìn)行以下配置:項(xiàng)目中引入Microsoft.Extensions.Hosting.WindowsServices包,本文給大家詳細(xì)講解,需要的朋友可以參考下2022-10-10Asp.net TextBox的TextChanged事件使用介紹
動(dòng)態(tài)創(chuàng)建的控件是如何加載視圖狀態(tài),還提到ProcessPostData方法的調(diào)用,這里我就用TextBox的TextChanged事件來說說視圖數(shù)據(jù)的加載以及事件的觸發(fā)2012-12-12Asp.net中斷點(diǎn)續(xù)傳的原理與實(shí)現(xiàn)方法分享
在了解HTTP斷點(diǎn)續(xù)傳的原理之前,讓我們先來了解一下HTTP協(xié)議,HTTP協(xié)議是一種基于tcp的簡單協(xié)議,分為請(qǐng)求和回復(fù)兩種2012-08-08基于.NET中:自動(dòng)將請(qǐng)求參數(shù)綁定到ASPX、ASHX和MVC的方法(菜鳥必看)
這篇文章的目的就是告訴初學(xué)者如何自動(dòng)將客戶端用AJAX發(fā)送的參數(shù)自動(dòng)綁定為強(qiáng)類型的成員屬性或方法參數(shù)2013-04-04.Net使用RabbitMQ即時(shí)發(fā)消息Demo
RabbitMQ是一個(gè)在AMQP基礎(chǔ)上完整的,可復(fù)用的企業(yè)消息系統(tǒng),下面這篇文章主要給大家介紹了關(guān)于.Net使用RabbitMQ即時(shí)發(fā)消息的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-07-07