8

我有一個MVC網絡應用程序,我正在使用DI簡單注入器。幾乎所有的代碼都被單元測試覆蓋。但是,現在我在某些控制器中添加了一些遙測電話,因此在設置依賴關係時遇到了問題。在單元測試中使用Application Insights?

遙測呼叫用於向Microsoft Azure託管的應用程序見解服務發送指標。該應用程序未在Azure中運行,只是具有ISS的服務器。 AI門戶告訴你關於你的應用程序的所有事情,包括你使用遙測庫發送的任何自定義事件。因此,控制器需要一個Microsoft.ApplicationInsights.TelemetryClient實例,該實例沒有Interface並且是一個密封的類,具有2個構造函數。我試着像這樣註冊它(複合式的生活方式是無關的這個問題,我只是包括它的完整性):

 // hybrid lifestyle that gives precedence to web api request scope 
     var requestOrTransientLifestyle = Lifestyle.CreateHybrid(
      () => HttpContext.Current != null, 
      new WebRequestLifestyle(), 
      Lifestyle.Transient); 

     container.Register<TelemetryClient>(requestOrTransientLifestyle); 

的問題是,由於TelemetryClient有2層構造,SI抱怨和驗證失敗。我發現一篇文章展示瞭如何覆蓋容器的構造函數解析行爲,但這看起來很複雜。首先,我想備份並詢問以下問題:

如果我不讓TelemetryClient成爲注入依賴項(只需在類中創建一個新的依賴項),那麼遙測將在每次單元運行時發送到Azure測試,創造大量的虛假數據?還是應用程序見解足夠聰明,知道它正在單元測試中運行,而不是發送數據?

任何「見解」到這個問題將不勝感激!

由於

+2

我不能與問題的AI側不禁註冊可以簡單地通過做註冊一個指向特定構造函數的委託:'container.Register(()=>新的TelemetryClient(/ *任何你想要定位的構造函數* /),'requestOrTransientLifestyle);'。另外檢查[DefaultScopedLifestyle](https://simpleinjector.readthedocs.org/en/latest/lifetimes.html#scoped) – qujck

+2

對於單元測試,您應該真正定義您自己的TelemetryClient抽象,您可以根據需要進行模擬。單元測試不應該與Azure交談。 – qujck

+0

您的自定義混合範圍令我擔憂。將網絡請求生活方式與短暫生活方式混合通常不是一種好的做法。它可以解釋爲什麼你需要這種混合的生活方式? – Steven

回答

14

Microsoft.ApplicationInsights.TelemetryClient,它沒有界面和是一個密封類,具有2個構造函數。

這個TelemetryClient是一個框架類型和framework types should not be auto-wired by your container

我發現了一篇文章,展示瞭如何覆蓋容器的構造函數解析行爲,但看起來相當複雜。

是的,這種複雜性是故意的,因爲我們想阻止人們用多個構造函數創建組件,因爲這是an anti-pattern

而不是使用自動佈線,可以作爲@qujck已經指出的那樣,簡單地進行以下注冊:

container.Register<TelemetryClient>(() => 
    new TelemetryClient(/*whatever values you need*/), 
    requestOrTransientLifestyle); 

或者是應用洞察足夠聰明,知道它在一個單元運行測試,而不是發送數據?

非常不可能。如果要測試依賴於此TelemetryClient的類,則最好使用虛擬實現,以防止單元測試變得易碎,速度慢或污染Insight數據。但即使測試不是問題,根據Dependency Inversion Principle,您應該依賴(1)由您自己的應用程序定義的(2)抽象。在使用TelemetryClient時,你失敗了兩點。

你應該做的,而不是爲定義一個(甚至多個),而忽視了尤其是對你的應用程序量身定製的TelemetryClient抽象。所以不要試圖用它可能的100種方法來模擬TelemetryClient的API,而只是在控制器實際使用的接口上定義方法,並儘可能簡化爲因此您可以使控制器的代碼更簡單 - 和 - 你的單元測試更簡單。

定義好抽象後,您可以創建一個適配器實現,該實現在內部使用TelemetryClient。我像你如下注冊這個適配器:

container.RegisterSingleton<ITelemetryLogger>(
    new TelemetryClientAdapter(new TelemetryClient(...))); 

在這裏,我假設TelemetryClient是線程安全的,可以作爲一個單獨的工作。否則,你可以做這樣的事情:

container.RegisterSingleton<ITelemetryLogger>(
    new TelemetryClientAdapter(() => new TelemetryClient(...))); 

這裏適配器仍是單身,但設置有委託,允許TelemetryClient的創建。另一個選擇是讓適配器在內部創建(並可能處置)TelemetryClient。這可能會使註冊變得更簡單:

container.RegisterSingleton<ITelemetryLogger>(new TelemetryClientAdapter()); 
+2

感謝您的詳細解答。正如Steve和@qujck所建議的那樣,我的主要問題是用簡單的一行代碼來回答我的選擇的構造函數(儘管Steve的答案有一個錯字 - container.Register是重複的)。在發佈這個問題後,我得到了與單元測試問題幾乎相同的解決方案 - 將TelemetryClient包裝在我自己設計的簡單類中,公開了我需要的一種方法,爲它創建了一個接口等。然後在單元測試中,那個簡單的包裝類是用Moq模擬的。 –

相關問題