2014-09-05 219 views
1

我正在爲視圖模型編寫單元測試。該應用程序使用Caliburn.Micro爲MVVM支持編寫。許多視圖模型依賴於Application.Current.Dispatcher,目的是將一些代碼分派到UI線程中。單元測試時掛在Application.Current.Dispatcher.Invoke(action)

創建應用程序對象從測試中我寫了下面的類:

public class AppInitializer { 
    private static Application app; 
    public static void InitApp() { 
     app = app ?? (app = Application.Current ?? new Application()); 
    } 
} 

我現在只是做了每個測試類如下:

[ClassInitialize] 
    public static void InitClass(TestContext ctx) { 
     AppInitializer.InitApp(); 
    } 

不幸的是,第一個電話從視圖模型中的Application.Current.Dispatcher掛起我的測試,直到超時。

我不想以某種方式抽象Application.CurrentDispatcher,我不想傳入視圖模型多一個模擬對象。如果可能的話,我想得到一些解決方法。

回答

3

我想你錯過了Application.Run的電話。你是對的,其中Application類的職責之一是在其當前正在執行的線程上創建並啓動Dispatcher,但所有這些都發生在調用Run期間。

而這正是麻煩開始了:Run是一個阻塞調用,即你的單元測試之前,不會退出Run執行。在商店應用程序中,有一個稱爲UITestMethod的特殊屬性,但我不認爲它在WPF中可用(尤其是如果您未使用MSTest)。

那麼你有什麼選擇?您可以在與您的單元測試運行的線程不同的線程上創建應用程序 - 但這會導致方法調用Join,因爲您必須查看是否將您的調用分派到其他線程。這對單元測試速度很慢。

甚至不能在您的單元測試執行的線程上手動創建調度程序 - 因爲它與前面提到的App類相同:Dispatcher.Run是阻塞調用。

這就是爲什麼我建議你爲Dispatcher創建一個抽象並注入它 - 它爲您節省了很多痛苦。

更新周圍語境:

在評論中,我提到了周圍語境作爲不依賴於注射的對象爲符合該Dispatcher抽象的視圖模型的解決方案。這是它會是什麼樣子代碼:

public interface IDispatcher 
{ 
    void ExecuteOnUIThread(Action action); 
    // Add whatever methods you need on this interface 
} 

public static class DispatcherContext 
{ 
    // An instance that implements IDispatcher can be accessed via this static property 
    public static IDispatcher Dispatcher { get; set; } 
} 

// Of course you need to write an adapter for the WPF Dispatcher class 

這樣你可以爲你的單元測試創​​建調度模擬,但仍然能夠通過在您的視圖模型的靜態屬性訪問此。您可以在http://blogs.msdn.com/b/ploeh/archive/2007/07/23/ambientcontext.aspx或Mark Seemann的優秀書籍Dependency Injection in .NET中瞭解有關此模式的更多信息。

+0

如果我添加app.Run(),測試在此調用上掛起。 – EngineerSpock 2014-09-05 06:58:04

+0

對不起,我還沒有完成我的答案。我已經更新了它。 – feO2x 2014-09-05 07:04:27

+0

不幸的是,我使用無處不在Caliburn的靜態方法Execute.OnUITread(Action action)。這將很難嘲笑。 – EngineerSpock 2014-09-05 07:13:08