2015-12-09 47 views
0

嗨!C#單元測試方法與MEF進口

我有一個類,它看起來像這樣:

public class ItemCollector { 
    public IKnownAuthority _DefaultAuthority = new DefaultAuthority(); 

    [ImportMany(typeof(IKnownAuthority))] 
    private IEnumerable<Lazy<IKnownAuthority, IKnownAuthorityMetadata>> _KnownAuthorities { get; set; } 

    public ItemCollector() { 
    var catalog = new AggregateCatalog(); 
    catalog.Catalogs.Add(new DirectoryCatalog(
       Path.Combine(Environment.CurrentDirectory, AUTHORITY_FOLDER))); 

    var container = new CompositionContainer(catalog); 
    container.ComposeParts(this); 
    } 

    public IEnumerable<Items> GetItems(string Auth) { 
    var Authority = _KnownAuthorities.FirstOrDefault(x => 
          x.Metadata.AuthorityName.ToLowerInvariant() == 
          Auth.ToLowerInvariant()); 

    if (Authority == null) 
     return _DefaultAuthority.GetItems(Address); 

    return Authority.Value.GetItems(Address); 
    } 
} 

正如你可以看到有一個方法(GetItems),我想進行單元測試這種方法。該方法只是試圖在MEF容器中查找一個值,並返回找到的項目或默認值的結果。 (所以我不想測試MEF的東西[我認爲別人已經做好了這:) :)我只是想測試,如果方法正確返回默認項或關於參數的導入項的值)

這是我的問題。我可以創建一個測試來檢查默認值,但我無法爲假定Directory(對於DirectoryCatalog)爲空的特定「Authority」創建測試。那麼是否有一種方法可以將Stub [IKnownAuthority]「注入」到測試方法中已經組成的目錄中?

我已經試過這樣的事情:

[TestMethod] 
public void GetItems_Ok() 
{ 
    ItemsCollector collector = new ItemsCollector(); 
    collector._DefaultAuthority = new StubDefaultAuthority_01(); 

    CompositionContainer container = new CompositionContainer(); 
    container.ComposeExportedValue<IKnownAuthority>(new StubAuthority_02()); 
    container.ComposeParts(collector); 

    var list = collector.GetItems("testing.test"));  
    Assert.IsTrue(list.Count() > 0); 
} 

[Export(typeof(IKnownAuthority))] 
[ExportMetadata("AuthorityName", "testing.test")] 
public class StubAuthority_02 : IKnownAuthority 
{ 
    public IEnumerable<Items> GetItems(string Auth) { 
    return new List<Items>() { new Items() }; 
    } 
} 

的StubAuthority_02不會找到它的方式向_KnownAuthorities集合。首先,我認爲原因是我在構造函數中編寫了部件,然後在測試方法中再次進行了重構(沒有設置屬性的重構)。但是,如果我從ItemCollector構造函數刪除

container.ComposeParts(this); 

線StubAuthority_02 isn't反正有。我相信這只是理解上的一個問題,但我無法弄清楚......

回答

0

以您嘗試做的方式合併兩個容器是不可能的。無論您何時調用ItemsCollector的構造函數,都會創建CompositionContainer並調用Compose/ComposeParts。這會導致MEF根據您提供的目錄構建其內部結構,以便在您請求時爲您提供正確的值。但是既然你在單元測試中說你的DirectoryCatalog是空的,MEF就不會找到任何導出來填充你的導入。

如果你的單元測試第二個CompositionContainer你會提供你的ItemsCollector的實例已經構建。這將導致MEF不爲其解決任何進口問題,只是將其視爲給定的。

所以,如果你想堅持使用ItemsCollector中的CompositionContainer封裝這個結構,那麼你的唯一方法就是在這兩種情況下對這個目錄進行單元測試。

另一種選擇是將CompositionContainer移動到ItemsCollector之外,並將目錄提供給您的容器。在應用程序代碼和單元測試代碼的情況下,目錄會有所不同。

如果你不想動CompositionContainer類的外部,也可以只通過一個目錄到您的ItemsCollector

... // Your ItemsCollector constructor 
public ItemsCollector(ComposablePartCatalog catalog) 
{ 
    var container = new CompositionContainer(catalog); 
    Container.Compose(this); 
} 

在你的應用程序使用的情況下,你可以這樣創建收集實例:

// application code 
var directoryCatalog = new DirectoryCatalog(
     Path.Combine(Environment.CurrentDirectory, AUTHORITY_FOLDER)); 
var collector = new ItemsCollector(directoryCatalog); 

在單元測試的情況下,你可以做這樣的事情:

// unit test code 
var catalog = new TypeCatalog(new Type[] {typeof(StubAuthority_02)}); 
var collector = new ItemsCollector(catalog); 

如果您不喜歡自己類型的公共API中的MEF類型,請創建自己的類型來包裝目錄。

希望這會有所幫助 - 將提供代碼,如果你認爲它會更好地解釋它。

+0

嗯,是否可以保存我在構造函數中使用的容器,然後重新組合保存的容器? 你認爲目前最好的方法是什麼?我喜歡這樣一個事實,即我的班級正在收集建築過程中需要的所有東西,但它不容易測試。 – Bado

+0

@Bado我建議你直接給'ItemsCollector'提供一個'Catalog'或者代表一個目錄的自定義類型。通過這種方式,您可以在兩種情況下輕鬆配置容器 - 更新答案。 –