Vous ne pouvez pas aimer ce qu'il peut impliquer ce que vous ressentez est un « hack laid », mais ma préférence est d'utiliser un vrai EventAggregator plutôt que tout se moquant. Bien qu'apparemment une ressource externe, l'EventAggregator fonctionne en mémoire et ne nécessite donc pas beaucoup d'installation, d'effacement et n'est pas un goulot d'étranglement comme d'autres ressources externes telles que des bases de données, des services web, etc. est approprié à utiliser dans un test unitaire. Sur cette base, j'ai utilisé cette méthode pour surmonter le problème de thread UI dans NUnit avec un minimum de changement ou de risque à mon code de production pour le bien des tests.
Tout d'abord, je créé une méthode d'extension comme ceci:
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;
}
}
Ensuite, dans tous mes abonnements d'événements j'utiliser les éléments suivants:
EventAggregator.GetEvent<MyEvent>().Subscribe
(
x => // do stuff,
ThreadOption.UiThread.MakeSafe()
);
Dans le code de production, ce juste fonctionne parfaitement.Pour fins de test, tout ce que je dois faire est d'ajouter dans mon set-up avec un peu de code de synchronisation dans mon 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"));
}
}
Pour le Je attente et pulsant plus succinct a également ajouté les méthodes d'extension suivantes à 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);
}
}
alors je peux faire:
// <snip>
aggregator.Subscribe(x => locker.Pulse(), ThreadOption.UIThread.MakeSafe());
myEvent.Publish("Testing");
locker.Wait(1000);
// </snip>
Encore une fois, si votre sensibilité signifie que vous voulez utiliser simulacres, allez-y. Si vous préférez utiliser la vraie chose, cela fonctionne.
Comment avez-vous répondu à cette question 2 ans après ma réponse avec la même solution et vous obtenez un crédit pour cela? :) –
Avez-vous mis à jour votre réponse? Je ne me souviens pas avoir vu l'EA se moquer de votre réponse ... – TTat
C'est la première ligne de ma réponse 'Mock eventAggregatorMock = nouveau Mock ();' –