2016-08-17 2 views
1

I haben folgende FunktionMVVM Licht Dispatcher Komponententests

public void Reset() 
{ 
    DisableModule(); 
    DispatcherHelper.UIDispatcher.Invoke(() => 
    { 
      PixelPointInfoCollection.Clear(); 
      PixelPointInfoCollection.Add(new PointViewModel()); 
    }); 
    _cachedPoints.Clear(); 
} 

Der folgende Code in der Invoke() Methode stecken bleibt, wenn eine Einheit Test läuft.

Ich sah einige Artikel über das Erstellen einer benutzerdefinierten Schnittstelle auf Dispatcher und mocking den Dispatcher in Komponententests. zum Beispiel http://blog.zuehlke.com/en/mvvm-and-unit-testing/

Gibt es keinen anderen Weg? Ich habe eine riesige Codebasis. Muss ich jetzt wirklich alles ändern?

aktualisieren 2016.08.18 jetzt hier Für das, was ich tat, und es ist

Arbeits
public static class AppServices 
{ 

    public static IDispatcher Dispatcher { get; set; } 
    public static void Init(IDispatcher dispatcher) 
    { 
     Dispatcher = dispatcher; 
    } 
} 

//this inteface is in order to overrcome MVVM light Dispatcher so we can mock it for unit tests 
public interface IDispatcher 
{ 
    void Invoke(Action action); 
    void Invoke(Action action, DispatcherPriority priority); 
    DispatcherOperation BeginInvoke(Action action); 
} 

public class DispatcherWrapper :IDispatcher 
{ 
    public DispatcherWrapper() 
    { 
     DispatcherHelper.Initialize(); 
    } 
    public void Invoke(Action action) 
    { 
     DispatcherHelper.UIDispatcher.Invoke(action); 
    } 

    public void Invoke(Action action, DispatcherPriority priority) 
    { 
     DispatcherHelper.UIDispatcher.Invoke(action, priority); 
    } 

    public DispatcherOperation BeginInvoke(Action action) 
    { 
     return DispatcherHelper.UIDispatcher.BeginInvoke(action); 
    } 
} 

so in den App.xaml.cs Ich nenne AppServices.Init (neu DispatcherWrapper());

und in den Unit-Tests ich rufe AppServices.Init (Stellvertreter.For());

NSubstitute mit

Bitte kommentieren, wenn Sie, was ich etwas fehle, ich bin besorgt darüber, wie mache ich das Mockframework die Aktionen laufen ich in der

DispatcherHelper.UIDispatcher.Invoke 
+0

Leider ja. Ihre Situation ist das Ergebnis eines anfänglichen Designproblems, das die Codebasis zu einem Komponententest machte. Die Schwierigkeit, Unit-Tests für Code zu erstellen, hängt direkt damit zusammen, wie gut der Code entworfen wurde. Der Artikel, den Sie in Ihrem Beitrag erwähnt haben, ist, was Sie tun müssten, um den Zugriff auf den Dispatcher mockfähig zu machen, da es (der Dispatcher) ein Implementierungsinteresse für den UI-Thread ist, das während Ihrer Komponententests nicht verfügbar ist. Daher die Sperre auf "Invoke" – Nkosi

Antwort

1

zu tun verwendet Unglücklicherweise ist die Situation auf ein anfängliches Designproblem zurückzuführen, das die Codebasis schwierig für den Unit-Test machte. Die Schwierigkeit, Unit-Tests für Code zu erstellen, hängt direkt damit zusammen, wie gut der Code entworfen wurde. Der Artikel, den Sie in Ihrem Beitrag erwähnt haben, ist, was Sie tun müssten, um den Zugriff auf den Dispatcher mockfähig zu machen, da es (der Dispatcher) ein Implementierungsinteresse für den UI-Thread ist, das während Ihrer Komponententests nicht verfügbar ist. Daraus ergibt sich die Sperre auf Invoke

Um den Artikel zu zitieren, die Sie erwähnt:

Wir sind nicht in der Lage, den Code zu testen, die App.Current.Dispatcher verwendet (seit App.Current wird null sein während Einheit Tests Ausführung).

Eine mögliche Lösung wäre eine Schnittstelle IDispatcher und einen Wrapper erstellen um App.Current.Dispatcher, die diese Schnittstelle implementiert.

public interface IDispatcher { 
    void Invoke(Action action); 
    void BeginInvoke(Action action); 
} 

public class DispatcherWrapper : IDispatcher { 
    Dispatcher dispatcher; 

    public DispatcherWrapper() {  
     dispatcher = Application.Current.Dispatcher;   
    } 
    public void Invoke(Action action) { 
     dispatcher.Invoke(action); 
    } 

    public void BeginInvoke(Action action) { 
     dispatcher.BeginInvoke(action); 
    } 
} 
+0

Wirklich, Ihr Ansichtsmodellcode sollte threadspezifische Ausführung an eine SynchronizationContext-Implementierung delegieren, die zur Laufzeit injiziert wird.In der Benutzeroberfläche können Sie den DispatcherSynchronizationContext verwenden und in Tests können Sie eine Implementierung zusammenhacken, die gleichzeitig im aktuellen Thread ausgeführt wird. – Will

+0

@Will Können Sie bitte eine vollständige Antwort mit einem Beispiel geben? – Gilad

+0

@Gilad wäre es im Wesentlichen das, aber mit SynchronizationContext anstelle von Dispatcher, und die Methodennamen würden anders sein. Es ist nicht so komplex. – Will

Verwandte Themen