2012-02-17 15 views
5

Estoy intentando seguir las sugerencias del Using the WPF Dispatcher in unit tests para ejecutar mi prueba nUnit.Método correcto para usar el Despachador de WPF en las pruebas unitarias

Cuando escribo mi unidad de prueba de la siguiente manera, funciona:

[Test] 
public void Data_Should_Contain_Items() 
{ 
    DispatcherFrame frame = new DispatcherFrame(); 
     PropertyChangedEventHandler waitForModelHandler = delegate(object sender, PropertyChangedEventArgs e) 
     { 
      if (e.PropertyName == "Data") 
      { 
      frame.Continue = false; 
      } 
     }; 
    _myViewModel.PropertyChanged += waitForModelHandler; 
    Dispatcher.PushFrame(frame); 

    Assert.IsTrue(_myViewModel.Data.Count > 0, "Data item counts do not match"); 
} 

Sin embargo, si trato de utilizar la sugerencia de la DispatcherUtil, no funciona:

[Test] 
public void Data_Should_Contain_Items() 
{ 
    DispatcherUtil.DoEvents(); 
    Assert.IsTrue(_myViewModel.Data.Count > 0, "Data item counts do not match"); 
} 

public static class DispatcherUtil 
{ 
    [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] 
    public static void DoEvents() 
    { 
     DispatcherFrame frame = new DispatcherFrame(); 
     Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, 
      new DispatcherOperationCallback(ExitFrame), frame); 
     Dispatcher.PushFrame(frame); 
    } 

    private static object ExitFrame(object frame) 
    { 
     ((DispatcherFrame)frame).Continue = false; 
     return null; 
    } 
} 

Cuando Estoy usando DispatcherUtil, parece que la llamada a ExitFrame sucede demasiado pronto, antes de que los datos estén listos.

¿No utilizo DispatcherUtil correctamente? Parece que es un mejor método para manejar el despachador en lugar de esperar las devoluciones de llamada del modelo de vista.

+0

¿Qué está tratando de probar, solo si el PropertyChangedEventHandler se invoca a la propiedad "datos"? Si es así, ¿por qué necesita involucrar al despachador? Tampoco uso _myViewModel aparte para adjuntar el controlador. – Phil

+0

@Phil: Cuando se crea una instancia de _myViewModel, su constructor realiza una llamada asyn. Cuando se complete esa llamada, _myViewModel.Data debería tener algunos valores. Estoy intentando comprobar que los datos están, de hecho, poblados, pero el hecho de que los datos se completen como resultado de una llamada asyn es lo que me está causando algunos problemas. Me gustaría evitar tener que escuchar eventos PropertyChanged en cualquier prueba de unidad que pueda tener que tratar con el Dispatcher. – Flack

Respuesta

7

Dado que el despachador es problemático en pruebas unitarias, mi solución sería romper la dependencia de su modelo de vista en el despachador. Asumo que las referencias que tienes actualmente no modificables como:

Dispatcher.CurrentDispatcher.BeginInvoke(.. 

El despachador es una dependencia externa y no deben ser parte de las pruebas unitarias - podemos asumir las obras despachador.

Usaría la inyección de dependencia (mans pobres, Unity, etc.). Crea una interfaz adecuada que represente al despachador. Crea una implementación real que envuelva al despachador real. Crea una implementación falsa que usa Action.BeginInvoke. En el falso, registra todos los IAsyncResults devueltos a las llamadas a BeginInvoke.
Luego, tenga un método auxiliar que esperaría a que se completaran todas las llamadas que puede usar en la prueba para esperar a que finalice.

O tenga una clase base de modelo de vista que haga lo mismo. Llama al despachador normalmente, pero se le puede indicar que llame a un simulacro durante las pruebas.

0

Encontré una solución fácil. En lugar de procesar Frames Async en Dispatcher, agregué otro método sincronizado a la clase DispatcherUtil. Al llamar a este DoEventsSync() - devuelve el método en que se procesan todos los marcos, creo que esto debería ayudar aquí:

public static class DispatcherUtil 
    { 
     [SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] 
     public static void DoEvents() 
     { 
      var frame = new DispatcherFrame(); 
      Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, 
       new DispatcherOperationCallback(ExitFrame), frame); 
      Dispatcher.PushFrame(frame); 
     } 

     public static void DoEventsSync() 
     { 
      var frame = new DispatcherFrame(); 
      Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Background, 
       new DispatcherOperationCallback(ExitFrame), frame); 
      Dispatcher.PushFrame(frame); 
     } 

     private static object ExitFrame(object frame) 
     { 
      ((DispatcherFrame)frame).Continue = false; 
      return null; 
     } 
    } 
Cuestiones relacionadas