Non è possibile, come questo in quanto può coinvolgere ciò che si sente è un "brutto hack", ma la mia preferenza è quella di utilizzare un vero e proprio EventAggregator piuttosto che beffardo tutto. Pur essendo apparentemente una risorsa esterna, EventAggregator viene eseguito in memoria e quindi non richiede molto set-up, clear down, e non è un collo di bottiglia come altre risorse esterne come database, servizi web, eccetera, e quindi lo sento è appropriato da usare in un test unitario. Su questa base ho usato questo metodo per superare il problema del thread dell'interfaccia utente in NUnit con modifiche minime o rischio al mio codice di produzione per il bene dei test.
In primo luogo ho creato un metodo di estensione in questo modo:
public static class ThreadingExtensions
{
private static ThreadOption? _uiOverride;
public static ThreadOption UiOverride
{
set { _uiOverride = value; }
}
public static ThreadOption MakeSafe(this ThreadOption option)
{
if (option == ThreadOption.UIThread && _uiOverride != null)
return (ThreadOption) _uiOverride;
return option;
}
}
Poi, in tutte le mie sottoscrizioni di eventi Io uso il seguente:
EventAggregator.GetEvent<MyEvent>().Subscribe
(
x => // do stuff,
ThreadOption.UiThread.MakeSafe()
);
Nel codice di produzione, questo solo funziona perfettamente.A scopo di verifica, tutto quello che devo fare è aggiungere questo nel mio set-up, con un po 'di codice di sincronizzazione nel mio test:
[TestFixture]
public class ExampleTest
{
[SetUp]
public void SetUp()
{
ThreadingExtensions.UiOverride = ThreadOption.Background;
}
[Test]
public void EventTest()
{
// This doesn't actually test anything useful. For a real test
// use something like a view model which subscribes to the event
// and perform your assertion on it after the event is published.
string result = null;
object locker = new object();
EventAggregator aggregator = new EventAggregator();
// For this example, MyEvent inherits from CompositePresentationEvent<string>
MyEvent myEvent = aggregator.GetEvent<MyEvent>();
// Subscribe to the event in the test to cause the monitor to pulse,
// releasing the wait when the event actually is raised in the background
// thread.
aggregator.Subscribe
(
x =>
{
result = x;
lock(locker) { Monitor.Pulse(locker); }
},
ThreadOption.UIThread.MakeSafe()
);
// Publish the event for testing
myEvent.Publish("Testing");
// Cause the monitor to wait for a pulse, but time-out after
// 1000 millisconds.
lock(locker) { Monitor.Wait(locker, 1000); }
// Once pulsed (or timed-out) perform your assertions in the real world
// your assertions would be against the object your are testing is
// subscribed.
Assert.That(result, Is.EqualTo("Testing"));
}
}
Per rendere l'attesa e pulsante più succinta Ho anche aggiunto i seguenti metodi di estensione a ThreadingExtensions:
public static void Wait(this object locker, int millisecondTimeout)
{
lock (locker)
{
Monitor.Wait(locker);
}
}
public static void Pulse(this object locker)
{
lock (locker)
{
Monitor.Pulse(locker);
}
}
Allora posso fare:
// <snip>
aggregator.Subscribe(x => locker.Pulse(), ThreadOption.UIThread.MakeSafe());
myEvent.Publish("Testing");
locker.Wait(1000);
// </snip>
Anche in questo caso, se la vostra sensibilità significa che si desidera utilizzare prende in giro, andare per esso. Se preferisci usare la cosa reale, funziona.
Com'è che hai risposto a questo 2 anni dopo la mia risposta con la stessa soluzione e ne ottieni il merito? :) –
Hai aggiornato la tua risposta? Non ricordavo di aver visto l'EA deriso nella tua risposta ... – TTat
È la prima riga della mia risposta 'Mock eventAggregatorMock = new Mock ();' –