2016-08-19 55 views
0

我試圖模擬(使用Moq)在第三方SDK中定義的類和接口。下面是他們的樣子一個簡單的例子:如何在模擬具體類時設置屬性

public interface IVehicle 
{ 
    string Type { get; } 
} 
public class Vehicle 
{ 
    public string Type { get; } 
} 
public class Jeep : Vehicle, IVehicle 
{ 
} 

我可以很容易地嘲笑界面,如下所示:

var mockVehicle = new Mock<IVehicule>(MockBehavior.Strict); 
mockVehicle 
    .SetupGet(v => v.Type) 
    .Returns("My custom vehicle"); 

,但我需要編寫特定於Jeep類單元測試,我可以」弄清楚如何嘲笑它。

我第一次嘗試:

var mockJeep = new Mock<Jeep>(MockBehavior.Strict);

產生以下錯誤:

Moq.MockException: IVehicle.Type invocation failed with mock behavior Strict. 
All invocations on the mock must have a corresponding setup. 

這是可以理解的,因爲我還沒有安裝的Type財產。

我的第二次嘗試:

var mockJeep = new Mock<Jeep>(MockBehavior.Strict); 
mockJeep 
    .SetupGet(v => v.Type) 
    .Returns("Jeep"); 

產生以下錯誤消息:

System.NotSupportedException: Invalid setup on a non-virtual (overridable in VB) member: v => v.Type 

這也是可以理解的,因爲Type財產上Vehicle類不是虛擬的。

所以我的問題是:有沒有辦法在嘲笑Jeep類時設置Type屬性?

+1

你可以不要模擬一個具體的類,至少不要使用moq。其次,你不需要模擬'Jeep'類來測試它。創建一個模擬的手段來創建一種*假*對象。 – meJustAndrew

+0

在這種情況下,最好的Moq可以做的是創建一個代理作爲'Jeep'的派生類,但是它不能覆蓋'Type'屬性。如果您有Visual Studio許可證級別來查看,微軟假冒與Moq很好地玩。其他解決方案也存在,但我能想到的那些解決方案將在您的單元測試中完全取代Moq,而不是增強它。我會重新寫這個答案。 – RavB

+0

@RAVB:謝謝你的提示,我會教給自己微軟假貨 – desautelsj

回答

2

最好的Moq可以在這種情況下創建代理作爲派生類Jeep但它不能覆蓋非虛擬Type屬性。請記住,當您嘗試使用Moq創建模擬時,框架將生成一個實現目標接口或從目標類派生的類。

Microsoft Fakes與Moq很好地協作,前提是您擁有Visual Studio許可證級別來訪問它。

1

由於這是一個相當普遍的問題,我將建議一種不同的方法。嘲弄/僞造/扼殺你不能控制的課程或界面通常很困難。此外,這通常會導致在您的模擬實例中複製其他人的功能。

一個更好的方法是將與外部庫的交互隔離到一個包裝類中,該包裝類可以控制接口和實現。這將允許您輕鬆地嘲笑/僞造/存根界面來測試界面的消費者。

這仍然留下了如何處理隔離層類的問題。爲此,您需要進行更多的集成測試,以便實際執行包裝類和外部類。然後,驗證您是否從您的類和要包裝的外部類的組合中獲得預期的行爲。

0

So my question is: is there a way to setup the Type property when mocking the Jeep class?

簡短的回答是否定的,你不能模擬使用Moq的非虛擬成員。

但是,我不認爲在這種情況下有任何理由來嘲笑任何事情,因爲沒有任何行爲來嘲笑。您在那裏的唯一成員是一個簡單的string屬性。所以,即使你可以,你可能不應該。

  • 如果你想測試Jeep類,只需直接 測試它沒有任何嘲諷其成員的;當你需要控制系統的依賴之一的行爲在測試(SUT)的嘲弄,才應使用
  • 如果你傳遞Jeep 類的另一種方法爲依賴,以測試方法,你可以只需要 new增加一個Jeep實例,並在你的測試中傳遞它;因爲 沒有任何跨界行爲(網絡通話等),所以 不需要嘲笑它。

你也可以考慮,如果是第二種情況,你應該能夠通過一個IVehicle而不是Jeep的,在這種情況下,嘲諷是放回桌子上(儘管如前所述,沒有明顯的必要在這種情況下)

順便說一句,您的層次結構看起來有點不合適 - 爲什麼Vehicle本身不實現IVehicle

public interface IVehicle 
{ 
    string Type { get; } 
} 
public class Vehicle: IVehicle 
{ 
    public string Type { get; } 
} 
public class Jeep : Vehicle 
{ 
} 

然後Jeep將已經是兩個車輛和IVehicle :)

+0

請記住,這不是我的*層次結構,它來自第三方SDK。我同意它看起來有點小,但不幸的是我無法改變它。其次,我的確將'Jeep'作爲依賴,這就是爲什麼我要嘲笑它。 – desautelsj

+0

夠公平 - 但吉普確實有任何跨越邊界的實際行爲?如果不是,你爲什麼需要嘲笑這種行爲? –

0

聲明:我在Typemock工作。

通過使用Typemock Isolator您將能夠模擬非虛擬方法,並且不需要更改您的代碼以便這樣做,在此特定示例中,您可以僞造Jeep的實例,然後修改其方法行爲。
這裏是惡搞Jeep測試的例子:

[TestMethod] 
public void CallFakeJeepType_WillReturnWantedString() 
{ 
    var jeep = Isolate.Fake.Instance<Jeep>(); 
    Isolate.WhenCalled(() => jeep.Type).WillReturn("fake jeep"); 

    var result = Jeep.DoSomthing(jeep); 

    Assert.AreEqual("fake jeep", result); 
} 

注:如果Jeep.Type有一個設置,以及你可以使用typemock的True property和測試看起來像這樣

var jeep = Isolate.Fake.Instance<Jeep>(); 
    jeep.Type = "fake jeep";