2012-01-20 11 views
13

Sto provando ad attivare il gestore di eventi assegnato al mio timer di simulazione. Come posso testare questo metodo privato qui?Come posso eseguire il gestore eventi assegnato a un mock?

public interface ITimer 
{ 
    void Start(); 
    double Interval { get; set; } 
    event ElapsedEventHandler Elapsed; 
} 

La classe client assegna un gestore di eventi a questo oggetto. Voglio testare la logica in questa classe.

_timer.Elapsed += ResetExpiredCounters; 

E il metodo assegnato è privato

private void ResetExpiredCounters(object sender, ElapsedEventArgs e) 
{ 
    // do something 
} 

voglio avere questo gestore di eventi nella mia finta ed eseguirlo in qualche modo. Come posso fare questo?

Aggiornamento:

ho capito che stava sollevando l'evento prima che ho assegnato il gestore di eventi. Ho corretto questo, ma ho ancora ottenere questo errore:

alzo in questo modo:

_timer.Raise(item => item.Elapsed += null, ElapsedEventArgs.Empty); 

o

_timer.Raise(item => item.Elapsed += null, EventArgs.Empty); 

Entrambi non funzionerà.

Aggiornamento:

Ecco la cosa che ha funzionato per me. Nota che non è utile se stai cercando di passare informazioni al gestore di eventi come Jon ha sottolineato nei commenti. Lo sto solo usando per deridere il wrapper per la classe System.Timers.Timer.

_timer.Raise(item => item.Elapsed += null, new EventArgs() as ElapsedEventArgs); 

Alla fine, questo non aiuterà affatto se avete bisogno di usare argomenti dell'evento dal momento che sarà sempre nullo. Tuttavia, è l'unico modo poiché ElapsedEventArgs ha solo un costruttore interno.

+0

"oggetto di tipo 'System.EventArgs' non può essere convertito in "System.Timers.ElapsedEventArgs" "- Quale parte non è chiara? – Groo

+0

Ancora ottengo quell'errore anche se passo ElapsedEventArgs. Come posso sollevare quell'evento? –

+0

È 'ElapsedEventHandler' nell'interfaccia' ITimer' in realtà 'System.Timers.ElapsedEventHandler'? – Groo

risposta

10

ElapsedEventArgs ha un costruttore privato e non può essere istanziata.

se si utilizza:

timer.Raise(item => item.Elapsed += null, new EventArgs() as ElapsedEventArgs); 

Poi il gestore sarà recevie un parametro nullo e perdere la sua proprietà SignalTime:

private void WhenTimerElapsed(object sender, ElapsedEventArgs e) 
{ 
    // e is null. 
} 

si potrebbe desiderare questo parametro in alcuni casi.

Per risolvere questo e renderlo più verificabili, ho anche creato un wrapper per il ElapsedEventArgs, e feci l'interfaccia usarlo:

public class TimeElapsedEventArgs : EventArgs 
{ 
    public DateTime SignalTime { get; private set; } 

    public TimeElapsedEventArgs() : this(DateTime.Now) 
    { 
    } 

    public TimeElapsedEventArgs(DateTime signalTime) 
    { 
     this.SignalTime = signalTime; 
    } 
} 

public interface IGenericTimer : IDisposable 
{ 
    double IntervalInMilliseconds { get; set; } 

    event EventHandler<TimerElapsedEventArgs> Elapsed; 

    void StartTimer(); 

    void StopTimer(); 
} 

L'implementazione si limiterà a fuoco il proprio evento ottenere i dati dal reale evento timer:

public class TimerWrapper : IGenericTimer 
{ 
    private readonly System.Timers.Timer timer; 

    public event EventHandler<TimerElapsedEventArgs> Elapsed; 

    public TimeSpan Interval 
    { 
     get 
     { 
      return this.timer.Interval; 
     } 
     set 
     { 
      this.timer.Interval = value; 
     } 
    } 

    public TimerWrapper (TimeSpan interval) 
    { 
     this.timer = new System.Timers.Timer(interval.TotalMilliseconds) { Enabled = false }; 
     this.timer.Elapsed += this.WhenTimerElapsed; 
    } 

    private void WhenTimerElapsed(object sender, ElapsedEventArgs elapsedEventArgs) 
    { 
     var handler = this.Elapsed; 
     if (handler != null) 
     { 
      handler(this, new TimeElapsedEventArgs(elapsedEventArgs.SignalTime)); 
     } 
    } 

    public void StartTimer() 
    { 
     this.timer.Start(); 
    } 

    public void StopTimer() 
    { 
     this.timer.Stop(); 
    } 

    public void Dispose() 
    { 
     this.Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if (!this.disposed) 
     { 
      if (disposing) 
      { 
       this.timer.Elapsed -= this.WhenTimerElapsed; 
       this.timer.Dispose(); 
      } 

      this.disposed = true; 
     } 
    } 
} 

Ora, è possibile semplificare e migliorare il mock di questo evento:

timer.Raise(item => item.Elapsed += null, new TimeElapsedEventArgs()); 

var yesterday = DateTime.Now.AddDays(-1); 
timer.Raise(item => item.Elapsed += null, new TimeElapsedEventArgs(yesterday)); 

Meno codice da scrivere, più facile da utilizzare e completamente disaccoppiato dal framework.

+1

+1 Bel lavoro ... –

+1

Semplicemente non riesco a capire perché ElapsedEventArgs abbia un costruttore privato, che richiede questa follia. grazie a tutti e due per la [email protected] JoanComasFdz, sarebbe utile se mostrassi la piena implementazione di TimerWrapper. Alla fine l'ho risolto, ma ci sono voluti alcuni minuti. – Brett

+0

@Brett Code updated :) – JoanComasFdz

5

Il Moq QuickStart guide ha una sezione sugli eventi. Credo che faresti usare

mock.Raise(m => m.Elapsed += null, new ElapsedEventArgs(...)); 
+0

Il gestore assegnato dalla classe client non sembra funzionare? Mi sto perdendo qualcosa? Non dovrei dire di prendere in giro salvare l'handler? –

+0

@ UfukHacıoğulları: Credo che il finto ricorderà solo i gestori che vengono aggiunti. Ti suggerirei di iniziare uscendo dal tuo codice * reale * e di sperimentare un evento di esempio fino a quando non avrai dimostrato come funziona Moq, quindi torna al tuo codice reale. –

+0

Sembra non eseguire affatto il gestore di eventi –

0

affrontato questo di recente, si può creare un ElapsedEventArgs utilizzando la riflessione:

public ElapsedEventArgs CreateElapsedEventArgs(DateTime signalTime) 
    { 
     var e = FormatterServices.GetUninitializedObject(typeof(ElapsedEventArgs)) as ElapsedEventArgs; 
     if (e != null) 
     { 
      var fieldInfo = e.GetType().GetField("signalTime", BindingFlags.NonPublic | BindingFlags.Instance); 
      if (fieldInfo != null) 
      { 
       fieldInfo.SetValue(e, signalTime); 
      } 
     } 

     return e; 
    } 

questo modo è possibile continuare a utilizzare il delegato ElapsedEventHandler originale

var yesterday = DateTime.Now.AddDays(-1); 
timer.Raise(item => item.Elapsed += null, CreateElapsedEventArgs(yesterday)); 
Problemi correlati