Moq基礎教程


1 基礎概念

集中精力開發業務邏輯部分,而不想在數據層上花費太多時間,這時,可以通過Mock對象來模擬數據層,而不必去為數據連接,CRUD,Mapping等等去做太多的事,而又可以使業務測試可以進行下去

2 教程及下載地址

http://code.google.com/p/moq/

http://www.codethinked.com/beginning-mocking-with-moq-3-part-1

http://www.cnblogs.com/jams742003/archive/2010/03/02/1676215.html

3 一個Demo

//接口
public interface ICustomer

{

void AddCall();

string GetCall();

string GetCall(string strUser);   

  }

var customer = new Mock<ICustomer>();    //建立Mock對象

//設置mock調用行為

customer.Setup(p=>p.AddCall());     

customer.Setup(p => p.GetCall()).Returns("phone:89898789");

customer.Setup(p => p.GetCall("Tom")).Returns("Hello");

//使用mock調用方法

customer.Object.AddCall();

Assert.AreEqual("phone:89898789", customer.Object.GetCall());

Assert.AreEqual("Hello", customer.Object.GetCall("Tom"));

4 功能點介紹

4.1 帶有引用或輸出參數的方法

string GetAddress(string strUser, out string Address);

string GetFamilyCall(ref string strUser);

var customer = new Mock<ICustomer>();

var outString="oo";

customer.Setup(p => p.GetAddress("", out outString)).Returns("shijiazhuang");

customer.Setup(p => p.GetFamilyCall(ref outString)).Returns("xx");

4.2 調用方法時拋出異常

方法:void ShowException(string str);

測試:

var customer = new Mock<ICustomer>();

customer.Setup(p => p.ShowException(string.Empty))

.Throws(new Exception("參數不能為空!"));

customer.Object.ShowException("");

4.3 調用時賦值

方法:void AddCall();

Mock測試:

var customer = new Mock<ICustomer>();

int iCount = 0;

customer.Setup(p => p.AddCall()).Callback(()=>iCount++);

Assert.AreEqual(0, iCount);

customer.Object.AddCall();

Assert.AreEqual(1, iCount);

customer.Object.AddCall();

Assert.AreEqual(2, iCount);

customer.Object.AddCall();

Assert.AreEqual(3, iCount);

4.4 Is<T>:匹配確定的給定類型

customer.Setup(x => x.SelfMatch(It.Is<int>(i => i % 2 == 0))).Returns("1");

方法SelfMatch接受int型參數,當參數為偶數時,才返回字符串1。

4.5 IsAny<T>:匹配給定的任何值

customer.Setup(p => p.SelfMatch(It.IsAny<int>())).Returns((int k) => "任何數:" + k);

方法SelfMatch接受int型,且任何int型參數都可以,然后返回:"任何數:" + k。

這里說明一下Returns方法:

Returns(Func<TResult>)

Returns<T>(Func<T,TResult>)

Returns<T1,T2>(Func<T1,T2,TResult>)

Returns<T1,T2,T3>(Func<T1,T2,T3,TResult>)

Returns<T1,T2,T3,T4>(Func<T1,T2,T3,T4,TResult>)

Returns(TResult)

4.6 IsInRange<T>:匹配給定類型的范圍

customer.Setup(p => p.SelfMatch(It.IsInRange<int>(0, 10, Range.Inclusive)))

.Returns("10以內的數");

方法SelfMatch接受int型,且當范圍在[0,10]時,才返回10以內的數

其中,這個方法,帶有一個包含與排除開關。

4.7 IsRegex<T>:正則匹配

customer.Setup(p => p.ShowException(It.IsRegex(@"^\d+$")))

.Throws(new Exception("不能是數字"));

4.8 設置屬性

public class Customer

{

public virtual int Unid { get; set; }

public virtual string Name { get; set; }

}

測試

var customer = new Mock<Customer>();

customer.Setup(p => p.Name).Returns("Tom");

customer.SetupProperty(p => p.Name, "tt");

4.9 Callbacks回調函數

當執行某方法時,調用其內部輸入的(Action)委托

看它的5種重載:

Callback(Action)

Callback<T>(Action<T>)

Callback<T1, T2>(Action<T1, T2>)

Callback<T1, T2, T3>(Action<T1, T2, T3>)

Callback<T1, T2, T3, T4>(Action<T1, T2, T3, T4>)

這個方法調用其內部輸入的Action委托,Aciton<T>有5種重載,所以這里的Callbacks有5種重載。

以第二個為例:

它的定義為:

ICallbackResult Callback<T>(Action<T> action)

這個表示一個輸入參數,

var customer = new Mock<ICustomer>();

customer.Setup(p => p.GetCall(It.IsAny<string>()))

.Returns("方法調用")

.Callback((string s)=>Console.WriteLine("ok"+s));

customer.Object.GetCall("x");

4.10 Verify 驗證

用於測試mock對象的方法或屬性是否被調用執行。當不需要測試結果時用到

重載很多:

Verify()

Verify(Expression<Action<T>>)

Verify<TResult>(Expression<Func<T, TResult>>)

Verify(Expression<Action<T>>, Times)

Verify(Expression<Action<T>>, String)

Verify<TResult>(Expression<Func<T, TResult>>, Times)

Verify<TResult>(Expression<Func<T, TResult>>, String)

Verify(Expression<Action<T>>, Times, String)

Verify<TResult>(Expression<Func<T, TResult>>, Times, String)

用其中三個舉例

第一個:Verify(),定義為:

public void Verify()

測試

public void TestVerify()

{

var customer = new Mock<ICustomer>();

customer.Setup(p => p.GetCall(It.IsAny<string>()))

.Returns("方法調用")

.Callback((string s) => Console.WriteLine("ok" + s))

.Verifiable();

customer.Object.GetCall("調用了!");

customer.Verify();

}

把Mock對象中的GetCall方法設置為可證實的(Verifiable()),

如果不調用粗體部分語句,那么customer.Verify()執行就不能通過。這個方法很有用。

第二個:Verify(Expression<Action<T>>),定義為:

public void Verify(Expression<Action<T>> expression)

customer.Verify(p => p.GetCall("call"));

如果沒有調用且輸入call字串的參數,則失敗。

第三個:Verify(Expression<Action<T>>, Times, String)

定義:

public void Verify( Expression<Action<T>> expression,

Times times,

string failMessage

)

這個比上一個多了兩個參數,一個用於表示調用次數相關描述,一個用於失敗的時打印信息

customer.Setup(p => p.GetCall(It.IsAny<string>()))

.Returns("方法調用")

.Callback((string s) => Console.WriteLine("ok" + s))

.Verifiable();

customer.Object.GetCall("call");

customer.Object.GetCall("call");

customer.Verify(p => p.GetCall("call"),

Times.AtLeast(2),"至少應被調用2次");

當GetCall方法被調用最少2次(且參數為call)時,測試成功。

方法中Times是個Times類類型,它有多個方法:

AtLeast

AtLeastOnce

AtMost

AtMostOnce

Between

Exactly

Never

Once

可以從語義上理解它們各自是什么意思,例如:AtLeast的定義為:

public static Times AtLeast(

int callCount

)

4.11 VerifyAll 驗證

在使用Verify方法時,只有被標記為可證實的(.Verifiable())的才可以驗證。

但VerifyAll會驗證所有的調用:

customer.Setup(p => p.GetCall(It.IsAny<string>()))

.Returns("方法調用")

.Callback((string s) => Console.WriteLine("ok" + s));

customer.Object.GetCall("call");

customer.VerifyAll();

4.12 Mock<T> Class

public class Mock<T> : Mock

where T : class

這的構造方法:

Mock<T>()

Mock<T>(MockBehavior)

Mock<T>(array<Object>[])

Mock<T>(MockBehavior, array<Object>[])

Mock的泛型實現類,它有很多方法和屬性。這里一一列舉。

(一)方法

(1)As<TInterface>方法

為mock添加接口實現(mock),可以給它指定設置。

在mock對象的屬性(或方法)首次使用之前才有效。且,參數只能是接口。

定義:

public virtual Mock<TInterface> As<TInterface>()

where TInterface : class

示例:

兩個接口:

其中的Icustomer接口還是前幾篇中用到的,這里添加一個Iorder接口:

public interface IOrder

{

string ShowTitle(string str);

}

Mock測試:

var customer = new Mock<ICustomer>();

customer.Setup(p => p.GetCall()).Returns("方法調用");

customer.Object.GetCall();

var order=customer.As<IOrder>();

order.Setup(p => p.ShowTitle(It.IsAny<string>())).Returns("ok");

Assert.AreEqual("ok",order.Object.ShowTitle(""));

這個將出現異常,因為在As之前,已經對GetCall進行了調用。

(2)SetUp方法

為模擬的對象中的方法指定設置,它有兩個重載:

Setup(Expression<Action<T>>)

Setup<TResult>(Expression<Func<T,TResult>>)

從兩個委托可以知道,這兩個一個是為沒有返回值的方法設置,一個是對有返回值的方法設置

public void TestSetUp()

{

var customer = new Mock<ICustomer>();

customer.Setup(p => p.AddCall())

.Callback(()=>Console.WriteLine("沒有返回值"));

customer.Setup(p => p.GetCall(It.IsAny<string>()))

.Returns("ok")

.Callback((string q) => Console.WriteLine("有返回值"));

customer.Object.AddCall();

customer.Object.GetCall("");

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM