Sie können dies nicht so, wie es bedeuten kann, was Sie fühlen, ist ein „hässlicher Hack“, aber meine Präferenz ist eher ein echtes Eventaggregator zu verwenden, als alles verspotten. Obwohl der EventAggregator angeblich eine externe Ressource ist, wird er im Speicher ausgeführt und benötigt daher nicht viel Setup, Cleardown und ist kein Flaschenhals wie andere externe Ressourcen wie Datenbanken, Web-Services und so weiter und daher fühle ich es eignet sich zur Verwendung in einem Komponententest. Auf dieser Grundlage habe ich diese Methode verwendet, um das UI-Thread-Problem in NUnit mit minimalen Änderungen oder Risiken für meinen Produktionscode zu umgehen.
Zum einen habe ich eine Erweiterungsmethode wie folgt:
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;
}
}
Dann in meinem Ereignisabonnements verwende ich die folgende:
EventAggregator.GetEvent<MyEvent>().Subscribe
(
x => // do stuff,
ThreadOption.UiThread.MakeSafe()
);
In Produktionscode, diese nur funktioniert nahtlos.Für Prüfzwecke, alles, was ich tun muß, ist dies mit einem bisschen Synchronisationscode in meinem Test in meinem Set-up hinzufügen:
[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"));
}
}
Um die Warte zu machen und pulsierend prägnant Ich habe auch die folgenden Erweiterungsmethoden zu 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);
}
}
Dann kann ich tun:
// <snip>
aggregator.Subscribe(x => locker.Pulse(), ThreadOption.UIThread.MakeSafe());
myEvent.Publish("Testing");
locker.Wait(1000);
// </snip>
Noch einmal, wenn Sie Ihre Gefühle meinen Sie Mocks verwenden möchten, gehen für sie. Wenn Sie lieber das echte Ding verwenden, funktioniert das.
Wie ist es, dass Sie das 2 Jahre nach meiner Antwort mit der gleichen Lösung beantwortet haben und Sie dafür Anerkennung bekommen? :) –
Hast du deine Antwort aktualisiert? Ich konnte mich nicht erinnern, den EA in deiner Antwort verspottet zu haben ... – TTat
Es ist die erste Zeile meiner Antwort 'Mock eventAggregatorMock = neuer Mock ();' –