2009-07-23 452 views
2

我讀,發現這個代碼作爲aswer一個問題單元測試動態加載代碼

public List<T> LoadPlugin<T>(string directory) 
{ 
    Type interfaceType = typeof(T); 
    List<T> implementations = new List<T>(); 

    //TODO: perform checks to ensure type is valid 

    foreach (var file in System.IO.Directory.GetFiles(directory)) 
    { 
     //TODO: add proper file handling here and limit files to check 
     //try/catch added in place of ensure files are not .dll 
     try 
     { 
      foreach (var type in System.Reflection.Assembly.LoadFile(file).GetTypes()) 
      { 
       if (interfaceType.IsAssignableFrom(type) && interfaceType != type) 
       { 
        //found class that implements interface 
        //TODO: perform additional checks to ensure any 
        //requirements not specified in interface 
        //ex: ensure type is a class, check for default constructor, etc 
        T instance = (T)Activator.CreateInstance(type); 
        implementations.Add(instance); 
       } 
      } 
     } 
     catch { } 
    } 

    return implementations; 
} 

,它讓我知道這個代碼將是什麼是最好的方式進行單元測試?

回答

1

在這一種方法中有三個不相關的事情(見SRP)。他們每個人應該分開他們自己的類,它實現了一些接口,所以你可以嘲笑他們更好的可測試性。樹的東西是:

  1. 從插件目錄中查找.dll文件。

  2. 加載.dll並獲取它所包含的類型。這應該是一個調用API方法的單線程。你不需要測試這個(至少不是在單元測試中),因爲你可以合理地假設編程語言的庫是正確的。

  3. 創建插件類型的實例。

當算法被分成三個部分,可以進行單元測試部分1和在隔離3(雖然在技術上爲部分1的測試不是單元測試,因爲它涉及的文件系統,除非C#有某種方式來模擬文件系統,比如Java 7的NIO2文件系統API應該是可以嘲弄的)。您也可以通過嘲諷第2部分來單元測試將它們放在一起的代碼。

0

我會以這樣一種方式抽象出動態程序集的檢測和加載,以便我可以嘲笑該部分並將測試程序集加載爲單元測試的一部分。

或者,由於您可以指定一個目錄,因此只需在您的計算機的TEMP目錄中構建一個臨時目錄作爲您的單元測試代碼的一部分,將單個測試項目中的一個程序集複製到該目錄中,插件系統掃描該目錄。

+1

在單元測試中建議使用文件系統是不是異端; – 2009-07-23 20:28:43

4

通過重構這樣說:

public List<T> LoadPlugin<T>(Type[] types) 
{ 
    Type interfaceType = typeof(T); 
    List<T> implementations = new List<T>(); 

    //TODO: perform checks to ensure type is valid 
    try 
    { 
     foreach (var type in types) 
     { 
      if (interfaceType.IsAssignableFrom(type) && interfaceType != type) 
      { 
       //found class that implements interface 
       //TODO: perform additional checks to ensure any 
       //requirements not specified in interface 
       //ex: ensure type is a class, check for default constructor, etc 
       T instance = (T)Activator.CreateInstance(type); 
       implementations.Add(instance); 
      } 
     } 
    } 
    catch { } 

    return implementations; 
} 
0

你沒有說你使用的單元測試框架,所以我會在這裏假設Visual Studio的內置功能。

您需要將有問題的程序集添加到部署項目列表中,以便將它們複製到單元測試工作目錄。

我看到的問題是在所有不同的實現上運行單元測試。在單個測試中測試所有實現會使得不清楚哪些實現失敗。你想爲每個實現運行一次測試。

但是,您可能會濫用數據驅動的測試機制來執行此操作。您可以在加載所有x實現(在您的Class_Initialize或其他)後在一些臨時數據庫表中插入數字0 ... x-1。 爲您的每個測試設置該表,並且測試將運行x次,並將數字作爲輸入數據。將其用作implementations列表(存儲在成員變量中)的索引並對該實現運行測試。

是的,非常醜陋..你失去了在不增加更多醜陋的情況下進行實際的數據驅動測試的能力。

1

我會將內部循環體抽取到一個方法中。我會努力使該方法返回T實例,如果測試失敗,則返回null。現在我可以編寫單元測試。現在

if (interfaceType.IsAssignableFrom(type) && interfaceType != type) 
    return (T)Activator.CreateInstance(type); 
else 
    return null; 

我能養活的,我期望返回一個非空實例這種新的功能類型和類型,我期待返回null。所有其他的代碼似乎都在使用系統調用,我傾向於相信這些。但是,如果你不這樣做 - 那就單獨測試一下。測試GetFiles()爲您提供正確的文件列表;測試GetTypes()可以給出給定文件中的正確類型等。