我希望我的應用程序捕獲異常並在生產環境中運行時發送電子郵件,但是當我運行我的MSTest單元測試時,我希望它能拋出異常,因此我可以進行調試。我現在的代碼總是通過電子郵件發送例外。有什麼方法可以知道當前進程是否由單元測試調用?有什麼方法可以確定它是否是測試(.NET)?
我能想到的一種方法是測試Enviroment.CurrentDirectory和類似的變量。有更好的嗎?
我希望我的應用程序捕獲異常並在生產環境中運行時發送電子郵件,但是當我運行我的MSTest單元測試時,我希望它能拋出異常,因此我可以進行調試。我現在的代碼總是通過電子郵件發送例外。有什麼方法可以知道當前進程是否由單元測試調用?有什麼方法可以確定它是否是測試(.NET)?
我能想到的一種方法是測試Enviroment.CurrentDirectory和類似的變量。有更好的嗎?
這是依賴注入或服務定位器的經典用例。不要硬連線你的應用程序來發送電子郵件,讓你的應用程序從服務定位器獲得一個通知服務,並讓它調用它。在生產中,配置服務定位器以返回電子郵件發送服務;在測試環境中,將其配置爲返回不執行任何操作的服務,或者將通知添加到列表或其他任何內容。
你不需要在這裏完整的重擊依賴注入:一個非常簡單的服務定位器就足夠了。同樣,可以通過測試夾具代碼來完成測試通知服務的注入。這裏是一個非常簡單的例子:
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或服務定位器的完整解釋,我的示例代碼絕不是最好的方法。
您可以使用編譯指令(如#IF DEBUG
)在測試時執行一些代碼片段,並在發佈模式下編譯時執行另一個代碼集;
另一種方法可以寫成不同的TraceListeners
,所以你可以用純文本記錄你的異常,或者通過在.config文件中設置發送你的電子郵件。
馬薩任何Emailsender類中, valeu pela dica – 2009-10-14 19:58:45
@Jader:écurintia – 2009-10-14 20:00:01
該方法不必要地混淆了代碼。最好的做法是嘲笑電子郵件發件人。 您可以在http://en.wikipedia.org/wiki/Mock_object – Vitaliy 2009-10-15 17:26:58
您也可以在您的App.Config上設置一個標誌,以便電子郵件例程驗證它是否應發送這些電子郵件,並在您的測試套件中將該標誌設置爲false。
閱讀更多內容是的,但我的測試套裝app.config是一個到我的應用程序app.config的軟鏈接。我將不得不復制它=)不是一個問題。 – 2009-10-14 20:04:31
SódáBrasil nessa thread – 2009-10-14 20:05:16
那麼,基本上你想要做的是在測試過程中模擬郵件發送。 我假設你有一個類,負責發送電子郵件,你的代碼看起來是這樣的:
..
..
catch(Exception e)
{
EmailSender.Send(e);
}
一個好的單元測試應該隔離,這意味着執行代碼不應該跨階級界限,更不用說發送消息給其他進程!
我建議你閱讀網上有關Mocking和Mocking框架的材料。 我個人使用RhinoMock框架。
你可以做一些真正的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。
但是,要直接回答你的問題,你可以在你的配置文件中使用一個標誌,但是這不是一個整潔或有用的標誌。
如果您更詳細地描述您的特定場景,這將有所幫助。
這聽起來不像你正在運行單元測試,如果他們發送電子郵件。代碼單元不應該被控制,或者確實具有任何具體錯誤報告類型的知識,除非它們可以在應用程序級別輕鬆配置。
這聽起來像你試圖做的是破解一些設置不正確的東西。如果問題不嚴重並且繼續是絕對安全的,那麼您應該使用記錄器,然後適當地對錯誤做出響應。如果你仍然需要這個功能(如果你沒有配置log4net,它不會做任何事情,因此你的測試不會發送任何郵件),那麼可能會有一個log4net appender發送郵件。
這不會影響可測試性,因爲您可以輸入錯誤條件,然後檢查是否已採取適當的響應(例如,「如果我無法讀取配置文件,則返回默認值」)。
如果您檢測到致命錯誤,那麼您應該查看應用程序頂級(即剛啓動後)創建異常處理程序。您可以通過爲AppDomain.UnhandledException事件配置處理程序來實現此目的。這樣,如果某個異常未處理,您仍然可以正確地捕獲異常,通過異常處理程序發送電子郵件,然後以優雅的方式終止應用程序。
您只會在正常的應用程序運行開始時安裝異常處理程序。除了最初的靴子搭配/佈線階段,您的應用程序不知道它。你的代碼單元不知道,你可以像往常一樣測試。
快速的方法可能是...
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塊
我不會亂丟我的代碼,像這樣的東西。使用配置文件來定義是否發送電子郵件。 – Fadeproof 2009-10-29 13:27:36
服務定位器確實是一個很好的解決方案。 – 2009-10-14 20:35:20
krystan:我不喜歡它們,因爲它們隱藏了依賴關係,並通過「獲取此服務,設置此服務」調用了應用程序代碼。基於構造器的DI意味着如果依賴關係不滿足,應用程序甚至不會構建。使用DI,可以輕鬆查看任何單獨的代碼,並瞭解它的依賴關係。 – 2009-10-14 20:50:44
馬克:公平點。我沒有詳細討論DI的原因是它比簡單的服務定位器方法需要更多的解釋,並且基於構造器的DI可能需要比服務定位器更多地重構原始發佈者的應用程序。但肯定有一個情況是,隨着情景變得更加複雜,正確的DI可能會更容易維護。 – itowlson 2009-10-14 21:16:36