.net測試篇之Moq行為配置
- 2019 年 10 月 3 日
- 筆記
我們前面說過.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它,而只需要給它重新賦值即可.