在GUI应用程序的上下文中,似乎有很多关于这类事情的建议。我想我的特殊情况已经够让我发问了。总结我的问题,你是如何测试事件的?
现在是长篇大论的解释我已经在服务点硬件上工作了一段时间了。这意味着我必须编写几个OPOS服务对象。在做了几年之后,我成功地编写了我的第一个COM可见C#服务对象,并将其投入生产。我尽力对整个项目进行单元测试,但发现这相当困难,但能够为大多数Service对象的接口实现提供良好的单元测试。最难的部分是事件的部分。当然,这个场景是我面临的最大也是最常见的场景,但在我的其他应用程序中,我遇到过类似的场景,在这些应用程序中,对事件的测试似乎有些尴尬。因此,设置场景的公共控制对象(CO)最多有5个事件,一个人也可以订阅。当CO调用Open方法时,查找Service (SO)并创建其实例,然后调用它的OpenService方法。SO的第三个参数是对CO的引用。现在我不需要定义整个CO,我只需要为这5个事件定义回调方法。msr定义的一个例子是
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsDual), Guid("CCB91121-B81E-11D2-AB74-0040054C3719")]
internal interface MsrControlObject
{
[DispId(1)]
void SOData(int status);
[DispId(2)]
void SODirectIO(int eventNumber, ref int pData, ref string pString);
[DispId(3)]
void SOError(int resultCode, int resultCodeExtended, int errorLocus, ref int pErrorResponse);
[DispId(4)]
void SOOutputCompleteDummy(int outputId);
[DispId(5)]
void SOStatusUpdate(int data);
} 我的OpenService方法会有这行代码
public class FakeMsrServiceObject : IUposBase
{
MsrControlObject _controlObject;
public int OpenService(string deviceClass, string deviceName, object dispatchObject)
{
_controlObject = (MsrControlObject)dispatchObject;
}
//example of how to fire an event
private void FireDataEvent(int status)
{
_controlObject.SODataEvent(status);
}
}因此,我想为了更好的测试,让我们做一个ControlObjectDispatcher。它将允许我将事件排队,然后在条件正确时将它们发送到CO。这就是我现在的位置。现在我知道了如何测试它的实现。但感觉不对。让我们以DataEvent为例。DataEvent的发射必须满足两个条件。首先,布尔属性DataEventEnabled必须为true,而另一个布尔属性FreezeEvents必须为false。此外,所有事件都是严格的FIFO。所以..。排队是完美的。既然我已经写好了这篇文章,我就知道实现是什么了。但是,为它写一个测试,使一个新的人对这个项目充满信心是困难的。考虑一下这个伪代码
[Test]
public void WhenMultipleEventsAreQueuedTheyAreFiredSequentiallyWhenConditionsAreCorrect()
{
_dispatcher.EnqueueDataEvent(new DataEvent(42));
_dispatcher.EnqueueStatusUpdateEvent(new StatusUpdateEvent(1));
Sleep(1000);
_spy.AssertNoEventsHaveFired();
_spy.AssertEventsCount(2);
_serviceObject.SetNumericProperty(PIDX_DataEventEnabled, 1);
_spy.AssertDataEventFired();
_spy.AssertStatusUpdateEventFired();
_serviceObject.GetnumericProperty(PIDX_DataEventEnabled).Should().BeEqualTo(0, "because firing a DataEvent sets DataEventEnabled to false");
}每个读到这个消息的人都会想,(不知道实现),我怎么知道,比如,在1分钟后,这个事件就会触发?我怎么知道那个疯狂的Robert人没有使用for循环,并且在迭代结束后忘记退出FireDataEvent例程?你真的不知道。当然你可以测试一下..。但这违背了单元测试的目的。
所以总结一下..。一个人是如何为事件编写测试的?每当.事件都会发生。它们有时需要更长的时间来处理和点火,而不是预期的时间。在我的第一次实现的集成测试中,如果我在断言调用了一个事件之前没有睡上50 my,那么测试就会失败。expected data event to have fired, but was never fired是为事件构建的测试框架吗?他们是否有任何共同的编码实践来涵盖这一点?
发布于 2015-04-13 08:38:44
您是否希望为您的事件进行单元测试或集成测试,这一点有点不清楚,因为您讨论了这两种情况。但是,考虑到问题上的标记,我将假设您的主要兴趣是从单元测试的角度来考虑。从单元测试的角度来看,如果您正在测试一个事件或一个正常的方法,那么它不会有太大的区别。单元的目标是隔离地测试单个功能块,因此在集成测试中使用sleep可能是有意义的(尽管我仍然试图避免它,并在可能的情况下使用其他类型的同步),但在单元测试中,我会把它当作一个标志,表明正在测试的功能没有被充分隔离。
所以,对我来说,有两片事件驱动的测试。首先,要测试在满足适当条件时是否触发了类触发的任何事件。其次,要测试事件的任何处理程序是否执行预期的操作。
测试处理程序的行为应该与您认为正确的任何其他测试相似。您将处理程序设置为预期的状态,设置您的期望,调用它,就像您是事件生成器一样,然后验证任何相关行为。
测试事件被触发本质上是一样的,设置您希望触发事件的状态,然后验证是否触发了适当填充的事件,以及发生了任何其他状态更改。所以,看看您的伪代码,我想说您至少有两个测试:
// Setup (used for both tests)
// Test may be missing setup for dispatcher state == data event disabled.
_dispatcher.EnqueueDataEvent(new DataEvent(42));
_dispatcher.EnqueueStatusUpdateEvent(new StatusUpdateEvent(1));
// Sleep(1000); // This shouldn’t be in a unit test.
// Test 1 – VerifyThatDataAndStatusEventsDoNotFireWhenDataEventDisabled
_spy.AssertNoEventsHaveFired();
_spy.AssertEventsCount(2);
// Test 2 – VerifyThatEnablingDataEventFiresPendingDataEvents
_serviceObject.SetNumericProperty(PIDX_DataEventEnabled, 1);
_spy.AssertDataEventFired();
_spy.AssertStatusUpdateEventFired();
// Test 3? – This could be part of Test 2, or it could be a different test to VerifyThatDataEventsAreDisabledOnceADataEventHasBeenTriggered.
_serviceObject.GetnumericProperty(PIDX_DataEventEnabled).Should().BeEqualTo(0, "because firing a DataEvent sets DataEventEnabled to false");查看测试代码,没有任何迹象表明您已经使用任何类型的for循环实现了实际的serviceObject,或者一分钟的测试将对serviceObject的行为产生任何影响。如果您没有从事件编程的角度考虑这一点,那么您是否真的在考虑调用SetNumericProperty是否会导致您使用‘a for循环,然后在迭代结束后忘记退出FireDataEvent例程?’如果是这样的话,那么要么SetNumericProperty不会返回,要么您有一个非线性实现,并且您可能在错误的地方测试错误的东西。如果没有看到事件生成代码,就很难通过…向您提供建议。
事件可以在任何时候触发…它们有时需要更长的时间来处理和燃烧,而不是预期的时间。
当您的应用程序正在运行时,这可能是正确的,但是当您正在进行单元测试时,它不应该是正确的。单元测试应该为定义的条件触发事件,并测试这些事件是否已被触发并正确生成。要实现这一点,您需要以单元隔离为目标,并且您可能必须接受一些瘦元素需要进行集成测试,而不是为了实现这种隔离而对单元进行测试。因此,如果您正在处理从应用程序外部触发的事件,您可能会遇到这样的情况:
public interface IInternalEventProcessor {
void SOData(int status);
void SODirectIO(int eventNumber, int pData, string pString);
};
public class ExternalEventProcessor {
IInternalEventProcessor _internalProcessor;
public ExternalEventProcessor(IInternalEventProcessor internalProcessor, /*Ideally pass in interface to external system to allow unit testing*/) {
_internalProcessor = internalProcessor;
// Register event subscriptions with external system
}
public void SOData(int status) {
_internalProcessor.SOData(status);
}
void SODirectIO(int eventNumber, ref int pData, ref string pString) {
_internalProcessor.SODirectIO(eventNumber, pData, pString);
}
}ExternalEventProcessor的目的是解耦外部系统上的依赖关系,以便更容易地对InternalEventProcessor中的事件处理进行单元测试。理想情况下,您仍然能够正确地对事件进行ExternalEventProcessor寄存器的单元测试,并通过提供外部系统和内部实现的模拟来传递,但是如果不能这样做,因为类被削减了仅对该类进行集成测试的最小值,这可能是一种现实的选择。
https://stackoverflow.com/questions/29545777
复制相似问题