.net測試篇之Moq行為配置
系列目錄
我們前面說過.Moq在建立模擬物件的時候,簡單物件賦值預設值,引用物件賦值為null,但是有些時候接口裡面還包含另一個介面物件,我們知道Moq是可以模擬一個介面物件的,我們可以通過配置讓Moq模擬所有可以Mock的物件.
我們新增如下程式碼
public interface IDtoWrapper { MyDto Dto { get; set; } string GetString(); } public interface IOutString { IDtoWrapper wrapper { get; set; } }
比如我們要模擬一個IOutString物件,如果不使用預設的行為則裡面的wrapper物件返回為Null,如果這樣我們沒法再繼續操作了.
我們可以對Moq的Default
行為進行配置,讓它對IDtoWrapper物件也進行Mock
測試程式碼如下
[Test] public void BehaviorConfig() { var moq = new Mock<IOutString>(); moq.DefaultValue = DefaultValue.Mock; Assert.NotNull(moq.Object.wrapper); }
以上測試會通過.這時候wrapper不再是null,我們便可以對其進行操作了.
我們通過除錯可以發現這時候不但IDtoWrapper不再是null,它裡面的Dto屬性也被賦值為一個new MyDto.是不是Mock框架可以模擬一個物件呢,實際上確實是可以,只不過是它模擬物件有很多限制,比如不能模擬不包含無參建構函式的物件,不能模擬不帶virtual或者abstract的方法等.如果我們對模擬的物件的方法沒有virual或者abstract修飾,這時候如果進行setup則會丟擲異常.
前面我們講的如何通過配置使mock自動mock遇到到層級可Mock物件.這一節我們來看另一個問題.
假如有這樣一種場:要mock的接口裡有一個Name屬性,在業務層我們要根據這個Name決定進入switch的不同分支裡面,但是回顧前面的章節,我們沒有遇到這種情況,我們都是隻是在mock物件建立時使用setup為要mock的物件的欄位設定值.很多人可能會想,可以通過moq物件實體的Object屬性把這個物件拿出來,然後改變它的值.我們來看看這樣做可行不可行.
我們有以下一個簡單介面
public interface ISt
{
string Name { get; set; }
int Age { get; set; }
}
測試方法如下
[Test]
public void BehaviorConfig()
{
var moq = new Mock<ISt>();
var obj = moq.Object;
obj.Name = "baidu";
Assert.NotNull(moq.Object.Name);
}
我們把moq的Object物件賦值給obj,然後通過obj改變Name值.我們斷言moq.Object.Name的值不為null,不幸的是,測試沒有通過.
按我們理解obj和moq.Object應該是引用型別,所以obj值的改變會引起moq.object值的改變,然而實際情況卻是我們一旦把moq.Objectm賦值給了obj,它們之間便脫離了關係.看來這樣是行不通的.
如何解決這個問題呢,其實moq例項物件裡面有一個SetupProperty方法,我們可以通過它來顯示指定哪些屬性會被跟蹤,如果屬性被跟蹤,則它的變化就會被記錄下來,而不像上面.
[Test]
public void BehaviorConfig()
{
var moq = new Mock<ISt>();
moq.SetupProperty(a => a.Name);
var obj = moq.Object;
obj.Name = "baidu";
Assert.NotNull(moq.Object.Name);
}
我們多加了一行程式碼,測試便可以通過了.
但是如果屬性很多,這樣一行一行幾乎重複的程式碼挺煩的,moq例項裡面還有一個SetupAllProperties
方法,這樣可以設定所有的屬性都被跟蹤.這樣如果多條需要這個值,我們便不需要每次都mock它,而只需要給它重新賦值即