2009-10-14 118 views
0

我希望我的應用程序捕獲異常並在生產環境中運行時發送電子郵件,但是當我運行我的MSTest單元測試時,我希望它能拋出異常,因此我可以進行調試。我現在的代碼總是通過電子郵件發送例外。有什麼方法可以知道當前進程是否由單元測試調用?有什麼方法可以確定它是否是測試(.NET)?

我能想到的一種方法是測試Enviroment.CurrentDirectory和類似的變量。有更好的嗎?

回答

2

這是依賴注入或服務定位器的經典用例。不要硬連線你的應用程序來發送電子郵件,讓你的應用程序從服務定位器獲得一個通知服務,並讓它調用它。在生產中,配置服務定位器以返回電子郵件發送服務;在測試環境中,將其配置爲返回不執行任何操作的服務,或者將通知添加到列表或其他任何內容。

你不需要在這裏完整的重擊依賴注入:一個非常簡單的服務定位器就足夠了。同樣,可以通過測試夾具代碼來完成測試通知服務的注入。這裏是一個非常簡單的例子:

public static class ServiceLocator 
{ 
    private static INotificationService _notificationService = new EmailNotificationService(); 

    public static INotificationService NotificationService 
    { 
    get { return _notificationService; } 
    } 

    // For test use only 
    public static void SetNotificationService(INotificationService notificationService) 
    { 
    _notificationService = notificationService; 
    } 
} 

(一個真正的服務定位器能夠提供一個更加靈活的接口,沿着IServiceContainer/IServiceProvider的的線條,讓你可以模擬出多種不同的服務,這僅僅是爲了說明這個想法。)

然後在您的測試代碼:

[SetUp] 
public void Setup() 
{ 
    ServiceLocator.NotificationService = new DiscardingService(); 
} 

(這裏使用對於被每個測試之前運行的方法NUnit的術語 - 不知道的MSTest相當於是什麼)

這樣做的一個好處是您現在可以測試是否正在發送正確的通知:讓您的測試通知服務捕獲列表中的通知,並且您可以對該列表進行斷言以驗證通知是否正在發送並且正確。

再一次請注意,這不是DI或服務定位器的完整解釋,我的示例代碼絕不是最好的方法。

+0

服務定位器確實是一個很好的解決方案。 – 2009-10-14 20:35:20

+3

krystan:我不喜歡它們,因爲它們隱藏了依賴關係,並通過「獲取此服務,設置此服務」調用了應用程序代碼。基於構造器的DI意味着如果依賴關係不滿足,應用程序甚至不會構建。使用DI,可以輕鬆查看任何單獨的代碼,並瞭解它的依賴關係。 – 2009-10-14 20:50:44

+0

馬克:公平點。我沒有詳細討論DI的原因是它比簡單的服務定位器方法需要更多的解釋,並且基於構造器的DI可能需要比服務定位器更多地重構原始發佈者的應用程序。但肯定有一個情況是,隨着情景變得更加複雜,正確的DI可能會更容易維護。 – itowlson 2009-10-14 21:16:36

1

您可以使用編譯指令(如#IF DEBUG)在測試時執行一些代碼片段,並在發佈模式下編譯時執行另一個代碼集;

另一種方法可以寫成不同的TraceListeners,所以你可以用純文本記錄你的異常,或者通過在.config文件中設置發送你的電子郵件。

+0

馬薩任何Emailsender類中, valeu pela dica – 2009-10-14 19:58:45

+0

@Jader:écurintia – 2009-10-14 20:00:01

+0

該方法不必要地混淆了代碼。最好的做法是嘲笑電子郵件發件人。 您可以在http://en.wikipedia.org/wiki/Mock_object – Vitaliy 2009-10-15 17:26:58

3

您也可以在您的App.Config上設置一個標誌,以便電子郵件例程驗證它是否應發送這些電子郵件,並在您的測試套件中將該標誌設置爲false。

+0

閱讀更多內容是的,但我的測試套裝app.config是一個到我的應用程序app.config的軟鏈接。我將不得不復制它=)不是一個問題。 – 2009-10-14 20:04:31

+0

SódáBrasil nessa thread – 2009-10-14 20:05:16

0

那麼,基本上你想要做的是在測試過程中模擬郵件發送。 我假設你有一個類,負責發送電子郵件,你的代碼看起來是這樣的:

.. 
.. 
catch(Exception e) 
{ 
    EmailSender.Send(e); 
} 

一個好的單元測試應該隔離,這意味着執行代碼不應該跨階級界限,更不用說發送消息給其他進程!

我建議你閱讀網上有關Mocking和Mocking框架的材料。 我個人使用RhinoMock框架。

1

你可以做一些真正的EVIL就像使用log4net和我們的IsDebug標誌。這當然是瘋狂的。

一個更好的方法是注入任何發送電子郵件到你的類,當你運行單元測試通過模擬對象這(moq)這樣的電子郵件將不會發送代碼時運行測試條件。

有這樣做的

起訂量 犀牛嘲笑

這幾個不錯的框架是我的兩個最愛。

當我談到注入我的意思是這樣

public class Foo 
{ 
    private Emailer _emailer; 

    public Foo(Emailer mailer) 
    { 
     _emailer = mailer; 
    } 

    public void SomeMethod() 
    { 
     ... 
     try 
     { 
      ... 
     } 
     catch(SomeException ex) 
     { 
      _emailer.SendEmail(ex); 
     } 
     finally 
     {} 
    } 
} 

的框架基本上是讓你在一定條件下的對象基本上是假的傳遞,然後可以將這些對象speicify行爲和使用斷言等在nunit。

但是,要直接回答你的問題,你可以在你的配置文件中使用一個標誌,但是這不是一個整潔或有用的標誌。

0

如果您更詳細地描述您的特定場景,這將有所幫助。

這聽起來不像你正在運行單元測試,如果他們發送電子郵件。代碼單元不應該被控制,或者確實具有任何具體錯誤報告類型的知識,除非它們可以在應用程序級別輕鬆配置。

這聽起來像你試圖做的是破解一些設置不正確的東西。如果問題不嚴重並且繼續是絕對安全的,那麼您應該使用記錄器,然後適當地對錯誤做出響應。如果你仍然需要這個功能(如果你沒有配置log4net,它不會做任何事情,因此你的測試不會發送任何郵件),那麼可能會有一個log4net appender發送郵件。

這不會影響可測試性,因爲您可以輸入錯誤條件,然後檢查是否已採取適當的響應(例如,「如果我無法讀取配置文件,則返回默認值」)。

如果您檢測到致命錯誤,那麼您應該查看應用程序頂級(即剛啓動後)創建異常處理程序。您可以通過爲AppDomain.UnhandledException事件配置處理程序來實現此目的。這樣,如果某個異常未處理,您仍然可以正確地捕獲異常,通過異常處理程序發送電子郵件,然後以優雅的方式終止應用程序。

您只會在正常的應用程序運行開始時安裝異常處理程序。除了最初的靴子搭配/佈線階段,您的應用程序不知道它。你的代碼單元不知道,你可以像往常一樣測試。

0

快速的方法可能是...

public static class Tester 
{ 
    public static bool InUnitTest 
    { 
     get 
     { 
      return Environment.StackTrace.IndexOf("Microsoft.VisualStudio.TestTools.TestTypes.Unit.UnitTestRunner") > 0; 
     } 
    } 
} 

然後在你的應用程序,你可以只

catch (Exception e) 
{ 
    if (!Tester.InUnitTest) 
    { 
     ... // send mail 
    } 
} 

您也實現您可能使用所以你不需要擔心改變每一個catch塊

+0

我不會亂丟我的代碼,像這樣的東西。使用配置文件來定義是否發送電子郵件。 – Fadeproof 2009-10-29 13:27:36

相關問題