2010-08-02 4 views
29

Ich beginne gerade mit TDD und konnte die meisten Probleme lösen, denen ich alleine gegenüberstand. Aber jetzt bin ich verloren: Wie kann ich überprüfen, ob Ereignisse ausgelöst werden? Ich suchte nach etwas wie Assert.Raise oder Assert.Fire, aber es gibt nichts. Google war nicht sehr nützlich, die meisten Hits waren Vorschläge wie foo.myEvent += new EventHandler(bar); Assert.NotNull(foo.myEvent);, aber das beweist nichts.Testereignisse mit Nunit

Vielen Dank!

Antwort

40

prüft Ereignisse ausgelöst wurden, können auf dieses Ereignis und abonnieren erfolgen und einen Booleschen Wert einstellen:

var wasCalled = false; 
foo.NyEvent += (o,e) => wasCalled = true; 

... 

Assert.IsTrue(wasCalled); 

Aufgrund Anfrage - ohne Lambda-Ausdrücke:

var wasCalled = false; 
foo.NyEvent += delegate(o,e){ wasCalled = true;} 

... 

Assert.IsTrue(wasCalled); 
0

Sie können Ihren benutzerdefinierten Ereignishandler hinzufügen, der beispielsweise ein ganzzahliges Feld in der Testfallklasse inkrementiert. Und dann prüfen, ob das Feld inkrementiert wurde.

1

Nicht wirklich das selbst getan, aber vielleicht könnten Sie dem Ereignis, das Sie abonnieren möchten, einen Dummy-Ereignishandler hinzufügen und eine lokale boolesche Variable aktualisieren lassen, so dass Sie nach dem Abfeuern der Methode den Status dieses Boolean überprüfen können, um zu sehen, ob das Ereignis ausgelöst wurde?

Etwas wie:

bool eventFired = false; 
foo.MyEvent += (s, e) => { eventFired = true }; 

Assert.IsTrue(eventFired); 
1

@theburningmonk: A ";" wird vermisst. Korrigierte Version ist:

bool eventFired = false; 
foo.MyEvent += (s, e) => { eventFired = true; }; 
Assert.IsTrue(eventFired); 

Prost! ;-)

+0

guten Fang! Ich würde meine Antwort aktualisieren, aber es gibt nicht viel Sinn, denn es ist wirklich eine duplex von Drors Antwort auf jeden Fall – theburningmonk

5

Ich musste das erst kürzlich machen, und unten ist das, was ich mir ausgedacht habe. Der Grund, warum ich nicht getan habe, was die anderen Posts gesagt haben, ist, dass ich die Idee eines Variablenhaltungszustandes nicht mag und ihn "manuell" zwischen mehreren Ereignissen zurücksetzen muss.

Unten ist der Code des ClassUnderTest mit NameChanged Ereignis, das in MyTests Tests getestet:

public class ClassUnderTest { 
    private string name; 
    public string Name { 
     get { return this.name; } 
     set { 
      if (value != this.name) { 
       this.name = value; 
       NameChanged(this, new PropertyChangedEventArgs("Name")); 
      } 
     } 
    } 

    public event EventHandler<PropertyChangedEventArgs> NameChanged = delegate { }; 
} 

[TestFixture] 
public class MyTests { 
    [Test] 
    public void Test_SameValue() { 
     var t = new ClassUnderTest(); 
     var e = new EventHandlerCapture<PropertyChangedEventArgs>(); 
     t.NameChanged += e.Handler; 

     Event.Assert(e, Event.IsNotRaised<PropertyChangedEventArgs>(),() => t.Name = null); 
     t.Name = "test"; 
     Event.Assert(e, Event.IsNotRaised<PropertyChangedEventArgs>(),() => t.Name = "test"); 
    } 
    [Test] 
    public void Test_DifferentValue() { 
     var t = new ClassUnderTest(); 
     var e = new EventHandlerCapture<PropertyChangedEventArgs>(); 
     t.NameChanged += e.Handler; 

     Event.Assert(e, Event.IsPropertyChanged(t, "Name"),() => t.Name = "test"); 
     Event.Assert(e, Event.IsPropertyChanged(t, "Name"),() => t.Name = null); 
    } 
} 

Die unterstützenden Klassen unten sind. Die Klassen können mit allen EventHandler<TEventArgs> oder erweitert zu anderen Delegaten verwendet werden. Ereignistests können verschachtelt werden.

/// <summary>Class to capture events</summary> 
public class EventHandlerCapture<TEventArgs> where TEventArgs : EventArgs { 
    public EventHandlerCapture() { 
     this.Reset(); 
    } 

    public object Sender { get; private set; } 
    public TEventArgs EventArgs { get; private set; } 
    public bool WasRaised { get; private set; } 

    public void Reset() { 
     this.Sender = null; 
     this.EventArgs = null; 
     this.WasRaised = false; 
    } 

    public void Handler(object sender, TEventArgs e) { 
     this.WasRaised = true; 
     this.Sender = sender; 
     this.EventArgs = e; 
    } 
} 

/// <summary>Contains things that make tests simple</summary> 
public static class Event { 
    public static void Assert<TEventArgs>(EventHandlerCapture<TEventArgs> capture, Action<EventHandlerCapture<TEventArgs>> test, Action code) where TEventArgs : EventArgs { 
     capture.Reset(); 
     code(); 
     test(capture); 
    } 
    public static Action<EventHandlerCapture<TEventArgs>> IsNotRaised<TEventArgs>() where TEventArgs : EventArgs { 
     return (EventHandlerCapture<TEventArgs> test) => { 
      NUnit.Framework.Assert.That(test.WasRaised, Is.False); 
     }; 
    } 
    public static Action<EventHandlerCapture<PropertyChangedEventArgs>> IsPropertyChanged(object sender, string name) { 
     return (EventHandlerCapture<PropertyChangedEventArgs> test) => { 
      NUnit.Framework.Assert.That(test.WasRaised, Is.True); 
      NUnit.Framework.Assert.That(test.Sender, Is.SameAs(sender)); 
      NUnit.Framework.Assert.That(test.EventArgs.PropertyName, Is.EqualTo(name)); 
     }; 
    } 
} 
8

Wenn Sie das Ereignis wissen synchron ausgelöst werden:

bool eventRaised = false; 
Customer customer = new Customer() { Name = "Carl" }; 
customer.NameChanged += (sender, e) => { eventRaised = true; }; 

customer.Name = "Sam"; 

Assert.IsTrue(eventRaised); 

Wenn das Ereignis ausgelöst asynchron sein kann:

ManualResetEvent eventRaised = new ManualResetEvent(false); 
Customer customer = new Customer() { Name = "Carl" }; 
customer.NameChanged += (sender, e) => { eventRaised.Set(); }; 

customer.Name = "Sam"; 

Assert.IsTrue(eventRaised.WaitOne(TIMEOUT)); 

jedoch einige asynchrone Verhalten testen sagen sollte Gemieden werden.

6

Ich ziehe es zu tun, wie folgt:

var wait = new AutoResetEvent(false); 
foo.MeEvent += (sender, eventArgs) => { wait.Set(); }; 
Assert.IsTrue(wait.WaitOne(TimeSpan.FromSeconds(5))); 

Vorteile: Unterstützt Multithreading-Szenario (falls Handler in anderen Thread aufgerufen wird)

+0

Ich nehme einen ähnlichen Ansatz, aber bevorzuge 'ManualResetEvent'. – Oliver

0

Mit NUnit und Moq Sie robustere Ereignis Tests tun können.

Mock-Klasse verwendete Ereignis zu überwachen auslöst:

public class AssertEvent { public virtual void Call(string obj) { } } 
Mock<AssertEvent> EventMock; 
AssertEvent Evt; 

-Setup für Ereignisauslöser:

[SetUp] 
public void TestInit() { 
    EventMock = new Mock<AssertEvent>(); 
    Evt= EventMock.Object; 
} 

Mock-Objekte in Tests verwenden:

[Test] 
public void TestMethod() { 
    myObject.Event1 += (sender, args) => Evt.Call("Event1Label"); 
    myObject.Event2 += (sender, args) => Evt.Call("Event2Label"); 
    myObject.Event3 += (sender, args) => Evt.Call("Event3Label");   

    myObject.SomeEventTrigger(); 

    EventMock.Verify(m => m.Call("Event1Label"), Times.Exactly(1)); 
    EventMock.Verify(m => m.Call("Event2Label"), Times.Never()); 
    EventMock.Verify(m => m.Call("Event3Label"), Times.Between(1,3); 

}