2011-12-14 9 views
7

Tenemos un componente COM hecho en casa escrito en C++. Ahora queremos probar sus funciones y eventos en un proyecto de prueba de C#. Las pruebas de función son bastante sencillas. Sin embargo, los eventos nunca se desencadenan.Unidad de prueba de eventos COM?

MyLib.MyClass m = new MyLib.MyClass(); 
Assert.IsTrue(m.doStuff()); // Works 

// This does not work. OnMyEvent is never called! 
m.MyEvent += new MyLib.IMyClassEvents_MyEventHandler(OnMyEvent); 
m.triggerEvent(); 

He buscado en Google esto y he leído sobre problemas similares aquí en StackOverflow. ¡He intentado todos los métodos propuestos pero no puedo hacerlo funcionar!

Hasta ahora he intentado ejecutar mi prueba con un active dispatcher pero sin éxito. También probé manualmente el bombeo de mensajes en el hilo principal usando Dispatcher.PushFrame(). Nada. Mis eventos nunca se disparan. Creé un proyecto simple de WinForms y verifiqué que mis eventos funcionan en una configuración normal. Por lo tanto, este problema solo se aplica a las pruebas unitarias.

P: ¿Cómo realizo una prueba de unidad C# normal que puede desencadenar con éxito controladores de eventos activos?

¡Alguien por ahí debería tener una muestra de trabajo! Por favor ayuda.

+0

Bien, la prueba de unidad falló. Los servidores COM tienden a necesitar el programa para bombear un ciclo de mensajes antes de que puedan generar eventos. Es parte del contrato de STA. Póngase en contacto con el autor del componente para obtener asistencia. –

+0

El servidor COM es nuestro propio componente, que queremos probar. Los mensajes de bombeo serán, como dices, esenciales. Entonces la pregunta permanece; ¿Cómo se logra esto en una prueba unitaria? – l33t

+0

@NOPslider ¿Qué marco de prueba unitario estás usando? Las versiones posteriores de NUnit se ajustan por defecto a un modelo de subprocesamiento MTA. – vcsjones

Respuesta

1

Si su objeto COM es un objeto STA, probablemente necesite ejecutar un bucle de mensaje para activar sus eventos.

Puede usar una pequeña envoltura alrededor del objeto Application y Form para hacer eso. Aquí hay un pequeño ejemplo que escribí en unos minutos.

Tenga en cuenta que no lo ejecuté ni lo probé, por lo que podría no funcionar, y la limpieza probablemente debería ser mejor. Pero puede darte una dirección para una solución.

Con este enfoque, la clase de prueba sería algo como esto:

[TestMethod] 
public void Test() 
{ 
    MessageLoopTestRunner.Run(

     // the logic of the test that should run on top of a message loop 
     runner => 
     { 
      var myObject = new ComObject(); 

      myObject.MyEvent += (source, args) => 
      { 
       Assert.AreEqual(5, args.Value); 

       // tell the runner we don't need the message loop anymore 
       runner.Finish(); 
      }; 

      myObject.TriggerEvent(5); 
     }, 

     // timeout to terminate message loop if test doesn't finish 
     TimeSpan.FromSeconds(3)); 
} 

Y el código para el MessageLoopTestRunner sería algo así:

public interface IMessageLoopTestRunner 
{ 
    void Finish(); 
} 

public class MessageLoopTestRunner : Form, IMessageLoopTestRunner 
{ 
    public static void Run(Action<IMessageLoopTestRunner> test, TimeSpan timeout) 
    { 
     Application.Run(new MessageLoopTestRunner(test, timeout)); 
    } 

    private readonly Action<IMessageLoopTestRunner> test; 
    private readonly Timer timeoutTimer; 

    private MessageLoopTestRunner(Action<IMessageLoopTestRunner> test, TimeSpan timeout) 
    { 
     this.test = test; 
     this.timeoutTimer = new Timer 
     { 
      Interval = (int)timeout.TotalMilliseconds, 
      Enabled = true 
     }; 

     this.timeoutTimer.Tick += delegate { this.Timeout(); }; 
    } 

    protected override void OnLoad(EventArgs e) 
    { 
     base.OnLoad(e); 

     // queue execution of the test on the message queue 
     this.BeginInvoke(new MethodInvoker(() => this.test(this))); 
    } 

    private void Timeout() 
    { 
     this.Finish(); 
     throw new Exception("Test timed out."); 
    } 

    public void Finish() 
    { 
     this.timeoutTimer.Dispose(); 
     this.Close(); 
    } 
} 

ayuda eso?