Unit testing 从对象测试事件

Unit testing 从对象测试事件,unit-testing,events,testing,tdd,Unit Testing,Events,Testing,Tdd,我一直在努力更多地了解TDD。目前,通过在控制台应用程序中执行大量的Debug.Asserts,使其保持简单 我想完成的部分测试是确保从对象引发事件的次数正确,因为客户端代码将依赖于这些事件 因此,我开始思考如何测试正在进行的活动,以及如何跟踪它们。所以我想出了一个监视器的“模式”(如果你可以这样称呼的话)。这基本上是一个在其构造函数中接受测试类型的对象的类 然后将事件连接到监视器,并创建委托,委托在引发事件时进行计数和记录 然后,我返回测试代码并执行如下操作: bool Test()

我一直在努力更多地了解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;
    }
这很好,当我故意破坏我的代码时,测试像预期的那样失败了,但是我想知道,这是不是太“自由形式”的测试代码(即没有支持测试的代码)?


其他想法

  • 你会测试比赛项目吗
  • 如何测试事件
  • 您认为上述内容是否有改进的余地

所有的意见都收到了<代码>^ ^

对我来说很好-因为它可以工作;-)

您可以做的最简单的事情是订阅一个匿名方法或lambda到事件,并在其中增加一个本地测试的计数器。根本不需要使用额外的类

我发现这不会让你的代码非常可读,所以我做了同样的事情。我已经在几个项目中编写了监视对象。通常它们比你的显示器更通用。它们只是公开您可以订阅事件的公共方法,并计算调用它们的次数。通过这种方式,您可以为不同的事件重用监控器对象

大概是这样的:

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

// testcode;

Assert.Equal( 1, monitor.EventsFired );
var eventHandlerStub = MockRepository.GenerateStub<IEventHandlerStub>();
myObject.Event += eventHandlerStub.Event;

// Run your code

eventHandlerStub.AssertWasCalled(x => x.Event(null, null));
问题是它不是真正通用的。您只能测试monitor.EventCatcher()可以订阅的事件。我通常不使用参数执行事件,所以这没有问题,我只使用标准的void EventCatcher(objectsender,EventArgs arguments)。您可以通过向事件订阅正确类型的lambda并在lambda中调用EventCatcher,使其更通用。这会让你的测试更难阅读。还可以使用泛型使EventCatcher方法与泛型EventHandler一起工作

您可能希望注意,最终您将希望能够准确地存储以什么顺序和参数调用的事件。你的eventmonitor很容易失控


我发现了另一种方法,对于具有更复杂断言的测试可能有意义

您不需要创建自己的监视器,而是让您选择的模拟框架为您创建监视器,您只需为处理事件的类创建一个接口。大概是这样的:

public interface IEventHandlerStub
{
    event EventHandler<T> Event(object sender, T arguments);
}
公共接口IEventHandlerStub
{
事件处理程序事件(对象发送方,T个参数);
}
然后您可以在测试中模拟这个接口。Rhino Mocks是这样做的:

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

// testcode;

Assert.Equal( 1, monitor.EventsFired );
var eventHandlerStub = MockRepository.GenerateStub<IEventHandlerStub>();
myObject.Event += eventHandlerStub.Event;

// Run your code

eventHandlerStub.AssertWasCalled(x => x.Event(null, null));
var eventHandlerStub=MockRepository.generateSub();
myObject.Event+=eventHandlerStub.Event;
//运行你的代码
AssertWasCalled(x=>x.Event(null,null));
对于这样一个简单的测试,这可能有点过头了,但是如果你想断言一些关于参数的东西,例如,你可以使用你的模拟框架的灵活性


另一方面。Rob和我正在开发一个通用的事件测试监视器类,这可能会使其中一些更容易。如果有人对使用类似的东西感兴趣,我想听听您的意见。

需要改进的一个漏洞/空间是,您的监视器只计算引发的事件数。理想情况下,可以指定应该引发哪些事件的预期、次数、顺序,甚至可以指定引发每个事件时(object sender、EventArgs e)对的外观。我做这件事的方法很简单

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

在我的测试类中,我只是将事件处理程序与声明的对象的事件挂钩。。。或者我在这里遗漏了什么东西???

我通常使用模拟对象来测试事件-在我使用Java时,我使用EasyMock(您选择的语言应该有类似的东西):

你所做的对我来说更像是一个存根——也就是说,监听器/观察者不知道应该多久调用一次,但只计算调用的数量,然后测试根据该数量进行断言。这也是一个合理的解决方案,您更喜欢哪一个主要取决于您个人的偏好,而且您的语言和可用工具可能会使哪一个解决方案更容易


看看。

我最近写了一系列关于发布同步和异步事件的对象的单元测试事件序列的博客文章。文章描述了单元测试方法和框架,并提供了完整的测试源代码

我描述了一个“事件监视器”的实现,这个“事件监视器”的概念与您所描述的和前面回答这个问题的人提到的类似。这无疑是正确的方向,因为在没有某种监控模式的情况下编写大量测试会导致大量杂乱的样板代码

使用本文中描述的事件监视器,可以编写如下测试:

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执行所有繁重的工作,并将运行测试(test),并断言事件是按照预期的顺序(expectedSequence)引发的。它还可以在测试失败时打印出漂亮的诊断消息。与所讨论的某些方法不同的是,它不仅计算,而且会断言已遵循指定的确切顺序。此外,您不需要钩住任何事件。所有这些都由事件监视器处理。因此,测试非常干净

帖子里有很多细节