2009-10-21 61 views
2

忍受我,我是NUnit的新手。我來自Rails之地,所以這對我來說是新的。NUnit Mocking不適用於Singleton方法

我有一個行代碼,看起來像這樣:

var code = WebSiteConfiguration.Instance.getCodeByCodeNameAndType("CATALOG_Brands_MinQty", item.Catalog); 

我想嘲笑它,像這樣(假設code已初始化):

var _websiteConfigurationMock = new DynamicMock(typeof(WebSiteConfiguration)); 
_websiteConfigurationMock.ExpectAndReturn("getCodeByCodeNameAndType", code); 

當我調試測試getCodeByCodeNameAndType正在返回null,而不是預期的code。我究竟做錯了什麼?

NUnit的版本:2.2.8

回答

1

一個DynamicMock創建內存一個新的對象,表示要嘲笑接口,或(從MarshalByRef繼承)類marshallable。

試試這個:

var _websiteConfigurationMock = new DynamicMock(typeof(WebSiteConfiguration)); 
_websiteConfigurationMock.ExpectAndReturn("getCodeByCodeNameAndType", code); 
WebSiteConfiguration conf = (WebSiteConfiguration)_websiteConfigurationMock.MockInstance; 
var x = conf.getCodeByCodeNameAndType("CATALOG_Brands_MinQty", item.Catalog); 

注意,第三行不會有工作,除非WebSiteConfiguration從MarshalByRef繼承。

你通常所做的是模擬一個接口並獲得一個實現這個接口的新對象,但是按照你配置它的方式行事,而不必去爲它創建一個具體的類型,所以我不完全確定你正在做的工作是否會起作用,除非你使用了一個更好的隔離框架,比如TypeMock,它可以攔截對現有對象中靜態方法/屬性的調用。

7

對不起,但我從來沒有用過NUnit.Mocks - 但我確實有一些NMock和Moq的經驗[順便說一下,我強烈推薦]。通常,您使用模擬庫來生成接口定義的代理,並且我假設NUnit.Mocks以相同的方式運行。

因此,如果您想嘲笑你單身,你將有可能做到以下幾點,

一個。創建一個接口,說

// All methods you would like to mock from this class, should 
// be members of this interface 
public interface IWebSiteConfiguration 
{ 
    // Should match signature of method you are mocking 
    CodeType getCodeByCodeNameAndType (
     string codeString, 
     CatalogType catalogType); 
} 

b。 「實施」界面

// You've already written the method, interface matches signature, 
// should be as easy as slapping interface on class declaration 
public class WebSiteConfiguration : IWebSiteConfiguration { } 

c。消費界面

好吧,所以步驟c。是你大部分工作的地方。從邏輯上講,如果你嘲笑你的單身人士,你實際上是對消費者進行單元測試[你從你的樣本中遺漏了的東西]。對於c。只需向消費者的ctor添加一個參數,或添加Type'IWebSiteConfiguration'的可公開訪問的屬性,然後在內部引用實例成員並針對此新接口調用您的方法。考慮這一點,

public class MyClass 
{ 
    public MyClass() { } 

    public void DoSomething() 
    { 
     // bad singleton! bad boy! static references are bad! you 
     // can't change them! convenient but bad! 
     code = WebSiteConfiguration.Instance.getCodeByCodeNameAndType (
      "some.string", 
      someCatalog) 
    } 
} 

成爲

public class MyClass 
{ 
    private readonly IWebSiteConfiguration _config = null; 

    // just so you don't break any other code, you can default 
    // to your static singleton on a default ctor 
    public MyClass() : this (WebSiteConfiguration.Instance) { } 

    // new constructor permits you to swap in any implementation 
    // including your mock! 
    public MyClass (IWebSiteConfiguration config) 
    { 
     _config = config; 
    } 

    public void DoSomething() 
    { 
     // huzzah! 
     code = _config.getCodeByCodeNameAndType ("some.string", someCatalog) 
    } 
} 

在單元測試中,創造了模擬,通過模擬消費者的參考,並測試消費者。

[Test] 
public void Test() 
{ 
    IWebSiteConfiguration mockConfig = null; 
    // setup mock instance and expectation via 
    // NUnit.Mocks, NMock, or Moq 

    MyClass myClass = new MyClass (mockConfig); 
    myClass.DoSomething(); 

    // verify results 
} 

這還用作實用介紹依賴注入[DI]。這只是將服務的引用(例如,您的網站配置類)傳遞或「注入」到消費者的做法,而不是讓消費者直接調用服務[例如通過靜態單例類]。

希望這有助於:)

+0

如果這可以刺激你的胃口,一些快速參考進一步閱讀。 Martin Fowler依賴注入[DI],控制反轉[IoC]和容器[http://martinfowler.com/articles/injection.html] Castle Windsor容器快速教程[http:// dotnetslackers。 com/articles/designpatterns/InversionOfControlAndDependencyInjectionWithCastleWindsorContainerPart1.aspx] – 2009-10-21 17:59:13

1

似乎沒有爲此使用反射一種解決方案,或許我完全誤解了這一點。

在此討論: http://www.geekbeing.com/2010/05/23/how-to-unit-test-singleton-hack-in-c

難道真的有效?上https://github.com/rbabreu/TestableSingleton

public class TestableSingleton : SingletonClass 
{ 
    public TestableSingleton() 
    { 
    FieldInfo fieldInfo = typeof(SingletonClass) 
     .GetField("_instance", 
     BindingFlags.Static | BindingFlags.NonPublic); 
    fieldInfo.SetValue(Instance, this); 
    } 
} 

項目速效其實我無法編譯它在Visual Studio中,因爲SingletonClass將有一個私人的構造函數。如果有人得到它的工作會很好,以避免適配器模式的開銷。