2011-05-04 10 views
8

Estoy buscando una manera de probar BeginInvoke en un método de acción, ya que el método se ejecuta en un subproceso en segundo plano no hay forma de saber cuándo se completa o llama al método de devolución de llamada. Estoy buscando una forma de mantener mi prueba en espera hasta que se llame a la devolución de llamada antes de hacer afirmaciones.Cómo probar BeginInvoke en una acción

En la siguiente clase Presenter, puede observar que estoy invocando PopulateView en el hilo de fondo que actualiza la vista cuando se obtienen datos y estoy tratando de afirmar la vista Las propiedades se inicializaron correctamente.

Estoy usando NUnit y Moq.

public class Presenter 
{ 
    private IView _view; 
    private IService _service; 
    public Presenter(IView view, IService service) 
    { 
     _view = view; 
     _service = service; 

     Action action = PopulateView; 
     action.BeginInvoke(PopulateViewCallback, action); 
    } 
    private void PopulateViewCallback(IAsyncResult ar) 
    { 
      try 
      { 
       Action target = (Action)ar.AsyncState; 
       target.EndInvoke(ar); 
      } 
      catch (Exception ex) 
      { 
       Logger.Instance.LogException("Failed to initialize view", ex); 
      } 
    } 

    private void PopulateView() 
    { 
      Thread.Sleep(2000); // Fetch data _service.DoSomeThing() 
      _view.Property1 = "xyz"; 
    } 
} 

Respuesta

9

Resuma su código para que pueda inyectar el comportamiento que desee en el momento de la prueba.

public class MethodInvoker 
{ 
    public virtual void InvokeMethod(Action method, Action callback) 
    { 
     method.BeginInvoke(callback, method); 
    } 
} 

Esta versión es asincrónica. A la hora de la prueba, sólo tiene que hacer una versión de bloqueo:

public class TestInvoker 
{ 
    public IAsyncResult MockResult { get; set; } 

    public override void InvokeMethod(Action method, Action callback) 
    { 
     method(); 
     callback(MockResult); 
    } 
} 

A continuación, el código simplemente cambia a esto:

// Inject this dependency 
Invoker.InvokeMethod(PopulateView, PopulateViewCallback); 

En tiempo de ejecución, es asincrónica. En el momento de la prueba, bloquea la llamada.

+0

Suena bien, lo he pensado y puedo usarlo para el nuevo código, pero ¿cómo puedo probar el código existente que llama a BeginInvoke directamente? – skjagini

+1

Refactor? La dependencia existe allí; la única forma de probar la unidad es abstraerla o no probarla. =/ – Tejs

+0

el parámetro 'callback' tiene que ser del tipo AsyncCallback. Pero gracias, esto es genial :) – soniiic

2

BeginInvoke() devuelve un IAsyncResult que se puede utilizar para esperar.

IAsynchResult ar = action.BeginInvoke(...); 
ar.AsyncWaitHandle.WaitOne(); 
+0

¿Cómo puedo obtener IAsynchResult (ar)? – skjagini

+0

es el valor de retorno de BeginInvoke. Puedes ver eso en el código. –

+0

Lo invoca desde el constructor y no tengo acceso desde fuera de la clase para escribir pruebas de unidades. – skjagini

0

No es necesario comprobar que los métodos se llaman en lugar ponen a prueba el resultado final - en este caso que _view.Propert1 == "xyz".

Como es una llamada asincrónica, es posible que necesite un bucle que confirme periódicamente que se ha establecido el valor, también es necesario un tiempo de espera en la prueba o el cheque; de ​​lo contrario, su prueba nunca fallaría (simplemente bloqueado).

Puede considerar anular la acción (PopulateView) para omitir el modo de suspensión.

Cuestiones relacionadas