2008-12-02 68 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