2016-04-18 5 views
1

Ich habe eine Anwendung, wo ich System.Threading.Timer verwenden. Wenn der Timer-Rückruf aufgerufen wird, muss ich etwas im UI-Thread tun. Ich benutze MVVMLight DispatcherHelper, um es zu tun. Es funktioniert einwandfrei, wenn ich eine Anwendung ausführe, aber wenn ich es Unit-Test (mit nUnit) testen, ruft der DispatcherHelper die Aktion nicht auf.Einheit testen MVVMLight DispatcherHelper mit Timer funktioniert nicht

es zu demonstrieren, erstelle ich einen einfachen Unit-Test

Timer _Timer; //this is System.Threading.Timer 
bool _DispatcherWorks; 

[Test] 
public async Task MVVMDispatcherTest() 
{ 
    DispatcherHelper.Initialize(); 
    Assert.That(DispatcherHelper.UIDispatcher, Is.Not.Null); 

    _Timer = new Timer(timerCallback, null, 500, 500); //start timer in 0.5 seconds and run every 0.5 seconds 

    Thread.Sleep(2000); //wait for timer to tick 

    Assert.That(_DispatcherWorks, Is.True); //will fail 
} 

private void timerCallback(object state) 
{ 
    Console.WriteLine("Timer tick"); 
    Assert.That(DispatcherHelper.UIDispatcher, Is.Not.Null); 
    DispatcherHelper.CheckBeginInvokeOnUI(() => 
    { 
     _DispatcherWorks = true; //this is never called 
    }); 
} 

Mein Timer 3 Mal ausgeführt wird, was erwartet wird. Die Aktion in DispatcherHelper.CheckBeginInvoikeOnUI wird jedoch nicht aufgerufen. Kann jemand vorschlagen, warum das nicht funktioniert und wie man das testbar macht?

Antwort

0

Ihr _Timer ist eine Instanz von System.Threading.Timer (nennen Sie diese Instanz timerA). In Ihrem Dispatchhelper wird eine neue Instanz von System.Threading.Timer erstellt (rufen Sie diese Instanz timerB auf). Dies sollte erklären, warum dein Test nicht funktioniert. Um dies testbar zu machen, gibt es ein paar Dinge, die Sie tun können. Sie könnten den Timer in Ihren Dispatch-Helfer injizieren, aber Sie müssten Ihren Produktionscode ändern, um Tests zu ermöglichen. Dem widerstehe ich normalerweise, wenn es geholfen werden kann.

Sie könnten auch Microsoft Fakes verwenden, um den System.Threading.Timer zu verspotten. Dies ist der Ansatz, den ich wählen würde.

0

Vielen Dank für Ihren Vorschlag, aber es würde nicht für mich arbeiten, weil ich tatsächlich testen möchte, was innerhalb des Timer-Ereignisses passiert, wenn es abläuft. Also hier, was ich getan habe.

  1. Ich nahm Quellcode von MVVMLight DispatcherHelper.cs, konvertierte diese Klasse von statischen in nicht statische Klasse und extrahierte Interface daraus. Ich übergebe Dispatcher als Parameter in den Konstruktor. In meinem ViewModelLocator initiiere ich meine neue DispatcherHelper-Klasse.
  2. Ich übergebe das gleiche DispatcherHelper-Objekt, das ich in ViewModelLocator an einen Konstruktor für jedes meiner ViewModels initiiert habe, wo ich einen Dispatcher benötigen würde. Dann benutze ich dieses Objekt im abgelaufenen Timer-Ereignis.
  3. In meinem Komponententest verwende ich nSubstitute, um die IDispatcherHelper-Schnittstelle nachzuahmen, und definierte die CheckBeginInvokeOnUI-Methode neu, um die übergebene Aktion einfach auszuführen, anstatt Dispatcher zu verwenden.
  4. Wenn ich nun mein ViewModel teste, übergebe ich diesen verspotteten DispatcherHelper an den ViewModel-Konstruktor, und ich kann testen, was innerhalb des Timers passiert.
Verwandte Themen