2010-05-17 139 views
4

幾個星期前,我跳上了MEF(ComponentModel)的潮流,現在我已經將它用於很多插件和共享庫。總的來說,除了我經常犯的錯誤之外,這很好,這導致調試會話令人沮喪。MEF和使用NUnit的單元測試

無論如何,我的應用程序運行良好,但是與MEF相關的代碼更改導致我的自動構建失敗。我的大部分單元測試都失敗了,因爲我測試的模塊依賴於需要由MEF加載的其他模塊。我通過繞過MEF並直接實例化這些對象來解決這些情況。

換句話說,通過MEF我會像

[Import] 
public ICandyInterface ci { get; set; } 

[Export(typeof(ICandyInterface))] 
public class MyCandy : ICandyInterface 
{ 
    [ImportingConstructor] 
    public MyCandy([Import("name_param")] string name) {} 
    ... 
} 

但在我的單元測試,我只想用

CandyInterface MyCandy = new CandyInterface("Godiva"); 

此外, CandyInterface需要連接到一個數據庫,我剛剛通過添加一個測試數據庫來解決這個問題到我的單元測試文件夾,我有NUnit用於所有的測試。

好了,這裏是我對於這種情況下的問題:

  1. 這是一個糟糕的方式來做事?
  2. 你會在[SetUp]
  3. 中推薦組成部分我還沒有學會如何在單元測試中使用mock - 這是一個很好的例子,我可能想要模擬底層的數據庫連接)只是返回虛擬數據,而不是真的需要數據庫?
  4. 如果您之前遇到類似的情況,您能提供您的經驗和解決問題的方式嗎? (或者這應該進入社區維基?)

回答

10

這聽起來像你在正確的軌道上。單元測試應測試單元,這就是您在直接創建實例時所要做的。如果你讓MEF爲你組合實例,他們將趨向於集成測試。並不是說集成測試有什麼問題,但是單元測試往往更易於維護,因爲您需要單獨測試每個單元。

You don't need a container to wire up instances in unit tests

我通常建議不要在SetUp中編寫Fixtures,因爲它會導致General Fixture反模式。

最好的做法是用Test Doubles替換依賴關係。動態模擬是這樣做的更多功能之一,所以絕對應該學習一些東西。

+0

這是一些非常好的建議,馬克。非常感謝您的意見! – Dave 2010-05-18 13:23:51

+0

我看過你發佈的文章,而且內容非常豐富。所以這聽起來像我目前的測試,創建SQLite依賴對象應該有另一個ICandyInterface測試存根(或模擬對象?我還沒有看到差異),只是返回任何數據,我希望從數據庫中獲得,沒有實際上使用數據庫。 – Dave 2010-05-18 13:53:26

+0

這聽起來正確,但:) – 2010-05-18 17:41:31

0

我同意手動創建DOC要比使用MEF組合容器來滿足導入要好得多,但是關於註釋'在安裝中合成固定裝置導致一般固定裝置反模式' - 我想提及的是,這並不總是案件。

如果您使用靜態容器並通過CompositionInitializer滿足導入。由於CompositionInitializer.Initialize不能被多次調用,因此SatisfyImport必須面對常規夾具反模式。但是,您始終可以創建CompositionContainer,添加目錄,並在容器上調用SatisyImportOnce。在這種情況下,你可以在每個測試中使用一個新的CompositionContainer,並且面臨共享/通用夾具反模式。

0

我寫了關於如何使用MEF進行單元測試(不是nunit,但工作原理相同)的博客。 訣竅是使用MockExportProvider,併爲我所有的測試創建了測試基礎以繼承。

這是我主要的自動裝配功能集成和單元測試工作:在我的崗位

protected void AutoWire(MockExportProvider mocksProvider, params Assembly[] assemblies){ 

CompositionContainer container = null; 

var assCatalogs = new List<AssemblyCatalog>(); 

foreach(var a in assemblies) 
{ 
    assCatalogs.Add(new AssemblyCatalog(a)); 
} 

if (mocksProvider != null) 
{ 
    var providers = new List<ExportProvider>(); 

    providers.Add(mocksProvider); //need to use the mocks provider before the assembly ones    

    foreach (var ac in assCatalogs) 
    { 
     var assemblyProvider = new CatalogExportProvider(ac);      
     providers.Add(assemblyProvider); 
    } 

    container = new CompositionContainer(providers.ToArray()); 

    foreach (var p in providers) //must set the source provider for CatalogExportProvider back to the container (kinda stupid but apparently no way around this) 
    { 
     if (p is CatalogExportProvider) 
     { 
      ((CatalogExportProvider)p).SourceProvider = container; 
     } 
    } 
} 
else 
{ 
    container = new CompositionContainer(new AggregateCatalog(assCatalogs)); 
} 

container.ComposeParts(this);   
} 

更多信息:https://yoavniran.wordpress.com/2012/10/18/unit-testing-wcf-and-mef/