转使用Moq让单元测试变得更简单
【ASP.Net MVC3 】使用Moq讓單元測(cè)試變得更簡(jiǎn)單
前幾天調(diào)查完了unity。現(xiàn)在給我的任務(wù)是讓我調(diào)查Moq。
以下是自己找了資料,總結(jié)并實(shí)踐的內(nèi)容。如果有表述和理解錯(cuò)誤的地方。懇請(qǐng)指正。
什么是Moq?
?
Moq(英語(yǔ)發(fā)音是Mock-you 或者只是mock)是一個(gè)針對(duì).Net開(kāi)發(fā)的模擬庫(kù),它從開(kāi)始就完全充分利用了.NET3.5(LINQ表達(dá)式樹(shù))和C#3.0的新特性(lambda表達(dá)式)。它的目標(biāo)是讓模擬以一種自然的方式與現(xiàn)有單元測(cè)試進(jìn)行集成,使它更加簡(jiǎn)單、直觀,以避免開(kāi)發(fā)人員被迫重寫測(cè)試或高成本的學(xué)習(xí)測(cè)試框架。這使它成為了一個(gè)高生產(chǎn)力、類型安全、重構(gòu)友好的模擬庫(kù)。
從哪得到Moq?
?
如果你看過(guò)我的其他文章,我們可以直接使用 VS中的插件Nuget來(lái)獲取Moq并且引用到指定的項(xiàng)目。
否則,我們可以從http://code.google.com/p/moq/這里得到Moq的最新版本。
可以模擬什么?局限性
?
首先,模擬的類不能是密封的。
其次,你不能直接模擬靜態(tài)方法。因?yàn)镸oq只能創(chuàng)建模擬對(duì)象實(shí)例。在這種情況下,間接的解決方案是我們可以在要模擬對(duì)象外包裝一層,并且去模擬這個(gè)新對(duì)象。這種模式被稱為適配器模式。
通常我們測(cè)試一個(gè)方法,它有可能調(diào)用好幾個(gè)service。但是每次都去訪問(wèn)這些service的代價(jià)是很高的。我們可以通過(guò)模擬的方法讓它模擬訪問(wèn)service,并且根據(jù)不同請(qǐng)求模擬返回響應(yīng)的結(jié)果。
Moq原理
?
Moq是如何辦到的?它只需要一個(gè)接口類型就可以生產(chǎn)一個(gè)對(duì)象?沒(méi)錯(cuò),就是這樣。Moq使用?Castle DynamicProxy?完成這個(gè)任務(wù)。基本原理就是它利用反射機(jī)制的?Emit?功能動(dòng)態(tài)生成一個(gè)空類型(也就是所有接口的方法都實(shí)例化,但是沒(méi)有任何功能,只是一個(gè)程序骨架)。所以Mock的能力就在于可以利用DynamicProxy的機(jī)制快速生產(chǎn)出一個(gè)假對(duì)象來(lái),用于模仿真對(duì)象的行為。
?
Moq中的重要成員
?
Mock
通過(guò)這個(gè)類,我們可以得到一個(gè)Mock<T>對(duì)象。T可以是接口,也可以是類。它有一個(gè)public 和virtual屬性。讓我們看看下邊的例子:
//define interface to be mockedpublic interface IFake
{
bool DoSomething(string actionname);
}
//define the test method
[TestMethod]
public void Test_Interface_IFake()
{
//make a mock Object by Moq
var mo = new Mock<IFake>();
//Setup our mock object
mo.Setup(foo => foo.DoSomething("Ping"))
.Returns(true);
//Assert it!
Assert.AreEqual(true, mo.Object.DoSomething("Ping"));
} 復(fù)制代碼
在上邊的代碼,我們通過(guò)傳遞泛型參數(shù)IFake去創(chuàng)建Mock<IFake>的實(shí)例 模擬接口IFake。
接下來(lái)我們要調(diào)用Setup()方法去創(chuàng)建我們的模擬對(duì)象。注意,Setup方法的參數(shù)是一個(gè)lambda表達(dá)式。我們可以這樣理解:當(dāng)被模擬的對(duì)象foo調(diào)用它自己的方法DoSomething(),并且參數(shù)是Ping。添加后綴?Return (true)我們可以理解為:前邊的請(qǐng)求返回結(jié)果為真。這是我們指定的返回值。當(dāng)一個(gè)請(qǐng)求調(diào)用DoSomething()方法時(shí)。如果傳入的參數(shù)是Ping,那么我們會(huì)返回true。接下來(lái),我們添加一個(gè)斷言,去判斷是否能得到預(yù)期結(jié)果。
注:Foo僅僅是一個(gè)詞用作通用替代真實(shí)的東西,特別是在討論技術(shù)想法和問(wèn)題.
It
?
這是一個(gè)靜態(tài)類,定義了靜態(tài)的泛型方法:Is<TValue>,?IsAny<TValue>,?IsInRange<TValue>, 和IsRegex。去過(guò)濾參數(shù)。看看下邊例子:
public interface IEmailSender{
bool Send(string subject, string body, string email);
}
[TestMethod]
public void User_Can_Send_Password()
{
var emailMock = new Mock<IEmailSender>();
emailMock
.Setup(sender => sender.Send(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns(true);
} 復(fù)制代碼
任何時(shí)候調(diào)用Send()方法,只要傳入的參數(shù)是任何的string,我們定義他會(huì)返回true。
我們也可以根據(jù)lambda的優(yōu)勢(shì)訂制一個(gè)規(guī)則:
? ? ? ? ??
var productRepository = new Mock<IProductRepository>();productRepository
.Expect(p => p.Get(It.Is<int>(id => id > 0 && id < 6)))
.Returns(newProduct.Object); 復(fù)制代碼
?
這樣我們可以設(shè)置這個(gè)id在0和6之間的時(shí)候才會(huì)返回一個(gè)新的對(duì)象。上邊提及到的其他方法,我們可以參考這里的教程?Moq’s QuickStart
此外,這個(gè)類的增強(qiáng)版是?Match<T>,你完全可以自定義模擬規(guī)則。
MockBehavior
?
這個(gè)類用于模擬對(duì)象的行為。就像是否按照默認(rèn)的模式去模擬。讓我們進(jìn)一步看看他的定義:
namespace Moq{
// Summary:
// Options to customize the behavior of the mock.
public enum MockBehavior
{
// Summary:
// Causes the mock to always throw an exception for invocations that don't have
// a corresponding setup.
Strict = 0,
//
// Summary:
// Will never throw exceptions, returning default values when necessary (null
// for reference types, zero for value types or empty enumerables and arrays).
Loose = 1,
//
// Summary:
// Default mock behavior, which equals Moq.MockBehavior.Loose.
Default = 1,
}
} 復(fù)制代碼
?
現(xiàn)在,看看如下例子:
?
var mock = new Mock<IFake>(MockBehavior.Strict);指定了mock行為是精準(zhǔn)的,如果沒(méi)有按照預(yù)期的Setup就會(huì)拋出異常。
MockFactory
?
這是一個(gè)模擬對(duì)象的工廠,我們不僅僅可以定制創(chuàng)建模擬對(duì)象的配置,也可以成批測(cè)試它們。看看下邊例子:
?
var factory = new MockFactory(MockBehavior.Strict) { DefaultValue = DefaultValue.Mock };// Create a mock using the factory settings
var fooMock = factory.Create<IFake>();
// Create a mock overriding the factory settings
var barMock = factory.Create<IEmailSender>(MockBehavior.Loose);
// Verify all verifiable expectations on all mocks created through the factory
factory.Verify(); 復(fù)制代碼
在前邊,我們已經(jīng)介紹了傳統(tǒng)方式的創(chuàng)建模擬對(duì)象,它不會(huì)去真正的調(diào)用方法,而是僅僅去執(zhí)行一些假設(shè):如果...那么返回... 。
在下邊的例子里,我將繼續(xù)介紹一些Mock<T> 類中基本并且重要的方法。
Verification
?
有時(shí)候,我們要確定一個(gè)方法是否被調(diào)用了,或者甚至要知道它被調(diào)用了多少次。一個(gè)比較傳統(tǒng)的方式是使用Verify()方法??纯聪逻吚?#xff1a;
mock.Verify(foo => foo.DoSomething("Ping"), Times.Once());上邊的代碼嘗試驗(yàn)證DoSomething("Ping")需要被調(diào)用,并且只調(diào)用一次。出了Once選項(xiàng),這里也有更多的選項(xiàng)可供你選擇去決定這個(gè)方法需要被調(diào)用多少次。如:?AtLeast,?AtLeastOnce,?AtMost,?AtMostOnce,?Between,?Equals,?Exactly,?Never, 和Once
一旦我們已經(jīng)模擬了對(duì)象,驗(yàn)證將是個(gè)輕松的任務(wù),看看下邊的例子:
[TestMethod]public void Test_FindByName_GetCalled()
{
// create some mock data
IList<Product> products = new List<Product>
{
new Product { ProductId = 1, Name = "C# Unleashed",
Description = "Short description here", Price = 49.99 },
new Product { ProductId = 2, Name = "ASP.Net Unleashed",
Description = "Short description here", Price = 59.99 },
new Product { ProductId = 3, Name = "Silverlight Unleashed",
Description = "Short description here", Price = 29.99 }
};
Mock<IProductRepository> mock = new Mock<IProductRepository>();
//mock
//.Setup(sender => sender.FindById(It.IsAny<int>()))
//.Returns((int s) => products.Where(
// x => x.ProductId == s).Single());
mock.Object.FindById(1);
mock
.Verify(x => x.FindById(1), Times.Once());
}
} 復(fù)制代碼
在上邊的例子里,有兩個(gè)地方值得注意。
首先是mock.Object.FindById(1)。為了在這個(gè)case里讓一切變得簡(jiǎn)單,我們直接調(diào)用mock.Object的方法。為什么呢?因?yàn)槲覀冞@個(gè)case只關(guān)注這個(gè)方法被調(diào)用的次數(shù),而不關(guān)注返回值。當(dāng)然,在實(shí)際的應(yīng)用中我們很少這樣做。
第二,你有沒(méi)有注意到被注釋掉的句子。它僅僅是一個(gè)“如果,那么”句子。意思是說(shuō),如果Setup 好了,就返回我們定義的值。
由于調(diào)查的時(shí)間有限,還有很多關(guān)于Mock給力的地方我沒(méi)有涉及到。我們可以去查看官方的資料。歡迎共同討論。
?
?
參考資料
http://stephenwalther.com/blog/archive/2008/06/12/tdd-introduction-to-moq.aspx
http://code.google.com/p/moq/wiki/QuickStart http://codeclimber.net.nz/archive/2009/10/23/10-resources-to-learn-moq.aspx http://blogs.clariusconsulting.net/kzu/tag/moq/http://blog.miniasp.com/post/2010/09/16/ASPNET-MVC-Unit-Testing-Part-03-Using-Mock-moq.aspx
http://dotnetslackers.com/articles/aspnet/Built-in-Unit-Test-for-ASP-NET-MVC-3-in-Visual-Studio-2010-Part-2.aspx#s4-using-moq-framework
轉(zhuǎn)載于:https://www.cnblogs.com/baiyu/archive/2012/01/11/2319588.html
總結(jié)
以上是生活随笔為你收集整理的转使用Moq让单元测试变得更简单的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 电信申请固定ip_各个代理ip之间的关系
- 下一篇: 一个莫名的人,竞标,教训,韩国女人