2010-08-02 204 views
29

我剛開始使用TDD,可以解決我自己面臨的大多數問題。但現在我迷路了:如何檢查事件是否被解僱?我正在尋找類似Assert.RaiseAssert.Fire的東西,但沒有什麼。谷歌並不是非常有用,大部分的點擊都是暗示foo.myEvent += new EventHandler(bar); Assert.NotNull(foo.myEvent);,但這沒有任何證據。使用nunit測試事件

謝謝!

回答

40

檢查,如果事件被解僱通過訂閱該事件並設置一個布爾值,可以做到:

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

... 

Assert.IsTrue(wasCalled); 

由於要求 - 不lambda表達式:

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

... 

Assert.IsTrue(wasCalled); 
0

您可以添加自定義事件處理程序,例如,在測試用例類中增加一些整數字段。然後檢查字段是否增加。

1

不是真的這樣做我自己,但也許你可以添加一個虛擬的事件處理程序到你想要訂閱的事件並讓它更新一個本地布爾變量,這樣在方法被觸發之後,你可以檢查該布爾的狀態以查看事件是否被觸發?

是這樣的:

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

Assert.IsTrue(eventFired); 
1

@theburningmonk:A 「;」不見了。更正版本爲:

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

乾杯! ;-)

+0

福氣!我會更新我的答案,但沒有多少意義,因爲它實際上是Dror的答案dupl – theburningmonk 2010-08-02 12:18:13

5

我最近不得不這樣做,下面是我想到的。我沒有做其他帖子說的原因是我不喜歡變量保持狀態的想法,並且不得不在多個事件之間「手動」重置它。

下面是在MyTests試驗測試的ClassUnderTestNameChanged事件的代碼:

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); 
    } 
} 

支持類如下。這些課程可以用於任何EventHandler<TEventArgs>或擴展到其他代表。事件測試可以嵌套。

/// <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

如果您知道事件將同步觸發:

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

customer.Name = "Sam"; 

Assert.IsTrue(eventRaised); 

如果事件可以異步觸發:

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)); 

然而,有人說測試異步行爲應應避免。

6

我喜歡做如下:

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

優點:支持多線程情況下(如果處理程序在不同的線程中調用)

+0

我採取了類似的方法,但更喜歡'ManualResetEvent'。 – Oliver 2015-09-10 11:36:31

0

使用NUnit和起訂量,你可以做更強大的事件檢測。

模擬類用於監視事件觸發:

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

設置爲事件觸發:

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

在測試使用模擬對象:

[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); 

}