There seems to be a ton of advice for this sorta thing in the context of a GUI application. I think my particular scenario is different enough to warrent me asking. To sum up my question how do you test events?
Now for the long winded explanation. I've worked with Point of Service hardware for a little while now. This means that I had to write a few OPOS Service Objects. After a few years of doing that I managed to write my first COM visible C# service object and put it into production. I tried my best to unit test the entire project but found it rather difficult, but was able to come up with good unit tests for most all of the Service Object's interface implementation. The hardest part was the Event's part. Granted this scenario was the biggest and most common one that I faced, but I've come across similar scenarios in other applications of mine where testing for an event just seemed awkward. So to set the scene the Common Control object (CO) has at most 5 events that a person can subscribe too. When the CO calls the method Open OPOS finds the Service Object (SO) and creates an instance of it, then calls it's OpenService method. The third parameter of the SO is a reference to the CO. Now I don't have to define the entire CO, I only have to define the call back method for those 5 events. An example of the msr's definition is this
[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);
}
and my OpenService method would have this line of code
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);
}
}
So I thought to myself for better testing, lets make a ControlObjectDispatcher. It will allow me to enqueue events, then fire them to the CO when conditions are correct. This is where I'm at. Now I know sorta how to test drive the implementation of it. But it just feels wrong. Lets take the DataEvent as an example. 2 conditions have to be met for a DataEvent to be fired. First the boolean property DataEventEnabled must be true, and the other boolean property FreezeEvents must be false. Also all events are strictly FIFO. So.. a Queue is perfect. And since I've written this before I know what the implementation will be. But writing a test for it that instills confidence to a new person to the project is difficult. Consider this pseudo code
[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");
}
Everyone reading this hear could wonder (without knowing the implementation) How do i know that say after 1 minute that this event fires? How do I know that that crazy Robert Snyder person didn't use a for loop and forget to exit the FireDataEvent
routine after the iterations were all up? You really don't. Granted you could test for a minute.. but that defeats the purpose of a unit test.
So to sum up again... How does a person write a test for events? Events can fire whenever.. and they can sometimes take longer to process and fire then expected. I've seen in my integration tests for the first implementation of this where if I didn't sleep for say 50ms before asserting that an event was called then the test would fail with something like. expected data event to have fired, but was never fired
Are their any test frameworks built for events? Are their any common coding practices that cover this?