2011-08-19 14 views
11

Ho un UserControl che pubblica un messaggio EventAggregator nel suo evento Loaded. Per testarlo (e ottenere l'evento Loaded) sto creando una finestra e aggiungendo il controllo ad esso, quindi sto aspettando che venga sollevato l'evento Loaded.Test del controllo WPF senza aggiungerlo a una finestra

C'è un modo per impostare un test in modo che l'evento Loaded si attivi senza dover creare e aggiungere il controllo a una finestra?

Ad esempio:

[Test, RequiresSTA] 
public void active_thingy_message_is_published_on_loaded() 
{ 
    const string TestMsg = "Active thingy changed"; 

    using (AutoResetEvent loadedEvent = new AutoResetEvent(false)) 
    { 
     DummyEventService eventService = new DummyEventService();     
     DummyControl control = new DummyControl(eventService, TestMsg); 
     control.Loaded += delegate { loadedEvent.Set(); }; 

     Assert.That(eventService.Message, Is.Null, "Before."); 
     Window window = new Window { Content = control }; 
     window.Show();     
     loadedEvent.WaitOne(); 
     window.Dispatcher.InvokeShutdown(); 
     Assert.That(eventService.Message, Is.EqualTo(TestMsg), "After."); 
    } 
} 

private class DummyControl : UserControl 
{ 
    public DummyControl(DummyEventService eventService, string testMsg) 
    { 
     Loaded += delegate { eventService.Publish(testMsg); }; 
    } 
} 

private class DummyEventService 
{ 
    public string Message { get; private set; } 
    public void Publish(string msg) { Message = msg; } 
} 

Aggiornamento

ho cambiato il titolo da "Unit Testing ..." a "Testing ...", e ha sostituito il tag "unit test "con" test ".

preferirei non spaccare il capello sopra esattamente quale classe di prova di questo è, in quanto non è costruttivo. Sì, si potrebbe sostenere che questo non è un "Test unitario", ma non è utile. Voglio testare un problema che dipende dal ciclo di vita del controllo e questo comporta l'evento Loaded. È un test di regressione importante, in quanto i componenti di terze parti di cui non ho il controllo dipendono dal fatto che il messaggio venga generato a Loaded.

Può caso Loaded essere sollevato senza aggiungere il controllo a una finestra?

+0

Dovrai simulare/falsificare la finestra ... Ma questo non sembra più il test unitario. –

+0

@Henk Come vedi una finta finestra che causa il caricamento del mio controllo utente, quindi attiva il suo evento 'Loaded'? –

+0

Si potrebbe provare ad aumentare l'evento Load attraverso la riflessione: http://stackoverflow.com/questions/198543/how-do-i-raise-an-event-via-reflection-in-net-c –

risposta

6

Se siete solo interessati a sparare l'evento Loaded del controllo di destinazione, quindi la riflessione dovrebbe fare il trucco.

public static void RaiseLoadedEvent(FrameworkElement element) 
{ 
    MethodInfo eventMethod = typeof(FrameworkElement).GetMethod("OnLoaded", 
     BindingFlags.Instance | BindingFlags.NonPublic); 

    RoutedEventArgs args = new RoutedEventArgs(FrameworkElement.LoadedEvent); 

    eventMethod.Invoke(element, new object[] { args }); 
} 

spara Questo letteralmente il metodo OnLoaded che è presente in ogni FrameworkElement, quindi se il vostro test richiede lo stato di applicazione, questo non funzionerà.

Inoltre, non v'è alcuna relazione tra l'evento Loaded di un genitore ed è bambini. Se un test richiede che gli elementi secondari attivino i propri eventi caricati, il metodo helper dovrà eseguire manualmente i controlli figlio e attivare anche quelli.

+0

+1 Speravo di non dover ricorrere alla riflessione, ma per il mio caso, questo non ha complicazioni. –

5

Riforma cosa contiene il gestore di eventi Loaded nel proprio metodo e chiama il gestore di eventi Loaded. Scrivi il tuo test unitario per testare il metodo refactored, non l'evento Loaded.

Il test dell'unità sta verificando che un'unità fa ciò che deve fare. Testare come l'unità interagisce con altre unità è il test di integrazione. È certamente utile eseguire test di integrazione e essere in grado di eseguire il test delle unità integrate di regressione, ma questo è un compito diverso dal test delle unità.

Infine: se non si ha la certezza che l'evento Loaded del controllo venga attivato quando viene caricato (che è la ragione principale per cui si eseguono test di integrazione come questo), qualcosa è molto sbagliato e si dovrebbe indagare.

+0

+1 per il commento "se non si ha fiducia". A volte è meglio non aderire dogmaticamente a una metodologia se non ti compra nulla. In questo caso, una revisione del codice è probabilmente sufficiente per verificare l'associazione degli eventi. –

+0

E se non lo è, c'è un problema che deve essere risolto da qualche parte. –

+0

@Robert @Merlyn Ho rimosso "Unità" dal titolo e dai tag, in modo che sia chiaro che voglio testare specificamente il problema senza impantanarmi su quale classe di test sia. Non sono affatto dogmatico. Il messaggio che viene pubblicato nell'evento 'Loaded' è molto importante in termini di ciclo di vita e come la messaggistica viene riprodotta con altri componenti di terze parti nel mio sistema su cui non ho * nessun * controllo.Sebbene sia allettante sfidare e analizzare la classe di test, non risolve il mio problema, che è un caso di test valido e importante. –

7

Negli ultimi due anni, forse le cose sono cambiate. Per motivi di copertura del codice, mi sono imbattuto in questo problema, ed è una soluzione.

WPF UIElements ereditano un metodo chiamato RaiseEvent, che prende un RoutedEventArgs. Questo può essere costruito usando il particolare UIElement's .LoadedEvent, che ti consente di ottenere l'ultimo bit di copertura del codice.

Dubito che abbia ancora bisogno della mia risposta, ma qualcuno potrebbe.

Problemi correlati