2011-12-14 16 views
7

Abbiamo un componente COM fatto in casa scritto in C++. Ora vogliamo testare le sue funzioni ed eventi in un progetto di test C#. I test di funzionalità sono piuttosto semplici. Tuttavia, gli eventi non vengono mai attivati.Unità test degli eventi 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(); 

Ho cercato su Google e ho letto su problemi simili qui su StackOverflow. Ho provato tutti i metodi proposti ma non riesco a farlo funzionare!

Finora ho provato a eseguire il test con un active dispatcher ma senza successo. Ho anche provato a pompare manualmente i messaggi nel thread principale usando Dispatcher.PushFrame(). Niente. I miei eventi non si innescano mai. Ho creato un semplice progetto WinForms e verificato che i miei eventi funzionassero in una normale configurazione. Quindi, questo problema si applica solo ai Test unitari.

Q: Come si effettua un normale test dell'unità C# che può attivare correttamente gestori di eventi attivi?

Qualcuno là fuori dovrebbe avere un campione funzionante! Per favore aiuto.

+0

Bene, il test dell'unità non è riuscito. I server COM tendono a richiedere al programma di pompare un loop di messaggi prima che possano generare eventi. Fa parte del contratto STA. Contattare l'autore del componente per il supporto. –

+0

Il server COM è il nostro componente, che vogliamo testare. I messaggi di pompaggio saranno, come dici tu, essenziali. Quindi la domanda rimane; come si ottiene questo risultato in un test unitario? – l33t

+0

@NOPslider Quale framework di test delle unità stai usando? Versioni successive di NUnit predefinite su un modello di threading MTA. – vcsjones

risposta

1

Se il proprio oggetto COM è un oggetto STA, è probabile che sia necessario eseguire un loop di messaggi per attivarne gli eventi.

È possibile utilizzare un piccolo avvolgimento attorno all'oggetto Application e Form per farlo. Ecco un piccolo esempio che ho scritto in pochi minuti.

Nota che non l'ho eseguito o testato, quindi potrebbe non funzionare e la pulizia dovrebbe probabilmente essere migliore. Ma potrebbe darti una direzione per una soluzione.

Usando questo approccio, la classe di test sarebbe simile a questa:

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

E il codice per il MessageLoopTestRunner sarebbe qualcosa di simile:

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

fa questo aiuto?