2008-12-02 66 views
2

我一直試圖讓更多的TDD。目前在控制檯應用程序中保留了很多簡單的Debug.Asserts從對象測試事件

我想完成的部分測試是確保從對象中提取事件的次數正確,因爲客戶端代碼將取決於這些事件。因此,我開始考慮如何測試事件被提出,以及我如何跟蹤它們。所以我想出了一個監視器「模式」(如果你可以稱之爲)。這基本上是一個在構造函數中接受測試類型的對象的類。

然後將事件連接到監視器,並創建委託,在事件引發時對這些委託進行計數和記錄。

然後我回到我的測試代碼和做一些事情,如:

bool Test() 
    { 
     MyObject mo = new MyObject(); 
     MyMonitor mon = new MyMonitor(mo); 

     // Do some tests that should cause the object to raise events.. 

     return mon.EventCount == expectedCount; 
    } 

這工作得很好,當我故意打掉我的代碼,測試失敗,因爲預期,但我不知道,這是太很多「自由形式」的測試代碼(即沒有支持測試的代碼)?


更多的想法

  • 你測試的事件?
  • 如何測試事件?
  • 你認爲上述有任何漏洞/改進空間?

感謝所有的輸入! ^_^

回答

4

你能做的就是訂閱匿名方法或lambda來的事件,並增加一個計數器局部於在它測試的最簡單的事情。根本不需要使用額外的課程。

我發現這不會讓你的代碼非常可讀,所以我完成了同樣的事情。我在幾個項目中編寫了監視器對象。通常它們比你的顯示器更具通用性。他們只公開公開的方法,您可以訂閱事件,並計算他們被調用的次數。這樣您可以重用監視器對象來處理不同的事件。

事情是這樣的:

MyObject subjectUnderTest = new MyObject(); 
EventMonitor monitor = new Monitor(); 
subjectUnderTest.Event += monitor.EventCatcher; 

// testcode; 

Assert.Equal(1, monitor.EventsFired); 

這裏的問題是,它不是真正的通用。您只能測試monitor.EventCatcher()可以訂閱的事件。我通常不會用參數做事件,所以這沒有問題,我只有標準的無效EventCatcher(對象發件人,EventArgs參數)。通過爲事件訂閱正確類型的lambda並在lambda中調用EventCatcher,可以使其更具通用性。這使得你的測試有點難以閱讀。您也可以使用泛型來使EventCatcher方法與泛型EventHandler一起工作。

你可能想看看,最終你會希望能夠確切地存儲以什麼順序和什麼參數調用的事件。您的事件監測器可能容易失控。


我發現了另一種做法,可能對更復雜的斷言測試有意義。

不是創建自己的顯示器,而是讓您的模擬框架爲您選擇,爲您創建一個處理事件的類的接口。像這樣:

public interface IEventHandlerStub 
{ 
    event EventHandler<T> Event(object sender, T arguments); 
} 

然後你可以在你的測試中模擬這個接口。犀牛嘲笑這是否是這樣的:

var eventHandlerStub = MockRepository.GenerateStub<IEventHandlerStub>(); 
myObject.Event += eventHandlerStub.Event; 

// Run your code 

eventHandlerStub.AssertWasCalled(x => x.Event(null, null)); 

對於這樣一個簡單的測試,這可能是矯枉過正,但如果你想斷言約例如參數的東西,你可以用它嘲弄的框架的靈活性。

另請注意。 Rob和我正在研究一個通用的事件測試監視器類,這可能會讓這些更容易一些。如果人們有興趣使用這樣的東西,我想聽聽你的意見。

0

看起來好像沒什麼問題 - 因爲它的工作原理;-)

1

一個洞/改善空間是你的顯示器只計算提出的事件的數量。理想情況下,人們可以指定應該提高哪些事件的期望值,次數,順序,以及甚至當每個事件引發時(對象發件人,EventArgs e)應該是什麼樣子。

+0

好一點,提出的案件是相當簡單的。在某些檢查中,如果滿足某些條件,它只會增加計數。 – 2008-12-02 14:40:45

0

我做測試事件。我這樣做很簡單。

private int counterEvent; 

[Test] 
public void abcTest() 
{ 
    counterEvent = 0; 
    //1- Initialize object to test 
    //2- Set the event to test (abcTest_event for example) 
    //Assert the object incremented (counterEvent for example) 
} 

private void abcTest_event(object sender) 
{ 
    counterEvent++; 
} 
0

在我的測試類,我只是在事件處理程序掛接到對象的事件聲明......還是我失去了一些東西?

+0

不會缺少任何東西..我這樣做,但我很快發現代碼變得混亂,(重置計數器等) - 封裝「監視」進入一個班級看起來是正確的事情。 – 2008-12-02 14:49:01

1

我通常測試使用模擬對象的事件 - 正如我在Java中工作,我用EasyMock的(類似的東西應該存在於你所選擇的語言):

FooListener listener = createMock(FooListener.class); 
expect(listener.someEvent()); 
replay(listener); 
myObject.addListener(listener); 
myObject.doSomethingThatFiresEvent(); 
verify(listener); 

你在做什麼聽起來更像對我來說是一個存根 - 也就是說,聽衆/觀察者不知道它應該被調用的頻率,而只是計算調用的次數,然後測試對這個計數作出斷言。這也是一個合理的解決方案,您更喜歡哪一個主要是個人喜好的問題 - 並且可能通過您的語言和可用工具使解決方案更容易。請參閱this article on the topic

2

我最近編寫了一系列關於發佈同步事件和異步事件的對象的單元測試事件序列的博文。這些文章描述了單元測試方法和框架,並提供了完整的源代碼和測試。

我描述了一個「事件監視器」的實現,這個事件監視器在概念上類似於你描述的和前面提到的這個問題的一些回答者。絕對是正確的方向,因爲編寫大量的測試沒有某種監視器模式導致大量雜亂的樣板代碼。

用在我的文章中描述的事件監控器,測試可以寫成像這樣:

AsyncEventPublisher publisher = new AsyncEventPublisher(); 

Action test =() => 
{ 
    publisher.RaiseA(); 
    publisher.RaiseB(); 
    publisher.RaiseC(); 
}; 

var expectedSequence = new[] { "EventA", "EventB", "EventC" }; 

EventMonitor.Assert(test, publisher, expectedSequence, TimeoutMS); 

的EventMonitor做所有繁重的任務,並運行測試(測試),並聲稱事件在升起預期序列(expectedSequence)。它還在測試失敗時打印出良好的診斷信息。與所討論的一些方法有所不同的是,它不僅僅算在內,它還會斷言指定的確切順序已被遵循。另外,您不需要掛鉤任何事件。這一切都由事件監視器處理。因此測試非常乾淨。

有一個在描述問題和方法,和源代碼太帖子很多細節:

http://gojisoft.com/blog/2010/04/22/event-sequence-unit-testing-part-1/

+0

不錯:)感謝分享! – 2010-06-04 07:29:47