2011-08-19 14 views
11

Tengo un UserControl que publica un mensaje EventAggregator en su evento Loaded. Para probar esto (y obtener el evento Loaded planteado), actualmente estoy creando una ventana y agregando el control a ella, esperando a que se genere el evento Loaded.Probando el control WPF sin agregarlo a una ventana

¿Hay alguna manera de configurar una prueba para que el evento Loaded se dispare sin tener que crear y agregar el control a una ventana?

Por ejemplo:

[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; } 
} 

actualización

he cambiado el título de "pruebas unitarias ..." a "Testing ...", y se sustituye la etiqueta "unidad- prueba "con" prueba ".

Preferiría no dividir los pelos sobre exactamente qué clase de prueba es esta, ya que no es constructiva. Sí, se podría argumentar que esto no es una "Prueba de unidad", pero eso no es útil. Quiero probar un problema que depende del ciclo de vida del control y esto implica el evento Loaded. Es una prueba de regresión importante, ya que los componentes de terceros que no tengo control dependen del mensaje que se plantea al Loaded.

¿Se puede generar el evento Loaded sin agregar el control a una ventana?

+0

Tendrá que fingir/burlarse de la ventana ... Pero esto ya no parece una prueba unitaria. –

+0

@Henk ¿Cómo ves una ventana falsa que hace que se cargue mi control de usuario y, por lo tanto, desencadena su evento 'Loaded'? –

+0

Usted podría intentar provocar el evento de carga a través de la reflexión: http://stackoverflow.com/questions/198543/how-do-i-raise-an-event-via-reflection-in-net-c –

Respuesta

6

Si solo está interesado en disparar el evento Loaded del control objetivo, entonces Reflection debería hacer el truco.

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 }); 
} 

dispara Esto, literalmente, el método OnLoaded que está presente en cada FrameworkElement, así que si su prueba requiere el estado de la aplicación, esto no va a funcionar.

Además, no hay relación entre el evento Loaded de un elemento primario y sus elementos secundarios. Si una prueba requiere que los elementos secundarios disparen sus eventos cargados, entonces el método de ayuda necesitará caminar manualmente los controles secundarios y dispararlos también.

+0

+1 Tenía la esperanza de no tener que recurrir a la reflexión, pero en mi caso, esto no tiene complicaciones. –

5

Refactorice lo que está en el controlador de eventos Loaded en su propio método, y haga que el controlador de eventos Loaded lo llame. Escriba su prueba unitaria para probar el método refactorizado, no el evento Loaded.

La prueba de unidad es verificar que una unidad hace lo que se supone que debe hacer. La prueba de cómo la unidad interopera con otras unidades es una prueba de integración. Ciertamente es valioso hacer pruebas de integración, y poder probar unidades integradas de regresión, pero esa es una tarea diferente de las pruebas unitarias.

Finalmente: si no tiene confianza en que el evento del control Loaded se activa cuando se carga (que es la razón principal por la que haría pruebas de integración como esta), algo está muy mal y debería investigarlo.

+0

+1 para el comentario "si no tiene confianza". A veces es mejor no adherirse dogmáticamente a una metodología si no le compra nada. En este caso, una revisión del código probablemente sea suficiente para verificar el enlace del evento. –

+0

Y si no lo es, hay un problema que debe solucionarse. –

+0

@Robert @Merlyn He eliminado "Unidad" del título y las etiquetas, por lo que está claro que quiero probar específicamente el problema sin empantanarme en la clase de prueba que es. No estoy siendo dogmático en absoluto. El mensaje que se publica en el evento 'Loaded' es muy importante en términos de ciclo de vida, y cómo los mensajes se reproducen con otros componentes de terceros en mi sistema que tengo * ningún * control.Aunque es tentador desafiar y analizar qué clase de prueba es esta, no resuelve mi problema, que es un caso de prueba válido e importante. –

7

En los últimos dos años, tal vez las cosas hayan cambiado. Por el bien de la cobertura del código, me encontré con este problema también, y es la solución.

WPF UIElements heredan un método llamado RaiseEvent, que toma un RoutedEventArgs. Esto se puede construir usando el .LoadedEvent particular de UIElement, lo que le permite obtener el último segmento de cobertura del código.

Dudo que todavía necesite mi respuesta, pero alguien puede.

Cuestiones relacionadas