Thanks to everyone for their suggestions. I got this to work in the end but it took a few false starts.
To refresh your memory, I have a component which provides async results. You call a method to start the process, lets call it Execute and then later you either get an OnSuccess or an OnError event firing. The events run in the main thread, via a call to Synchronize.
- So step one was to have those event handlers both set a variable that either indicates success or failure. In addition, they also set a TEvent call FCompleted.
- My test code looks like this (a few names changed to protect the innocent):
var
LStartTime : TDateTime;
begin
FResult := Pending;
ComponentUnderTest.Execute(Value);
LStartTime := Now;
while (FCompleted.WaitFor(10) = wrTimeOut) and (MillisecondsBetween(Now, LStartTime) < TIMEOUT_MS) do
begin
System.Classes.CheckSynchronize;
end;
Assert.IsFalse(MillisecondsBetween(Now, LStartTime) >= TIMEOUT_MS, 'Test timed out waiting for the asynchronous operation to complete');
Assert.AreEqual(Success, FResult);
Assert.AreEqual(Expected, ComponentUnderTest.RecordCount);
end;
Ordinarily I shouldn’t be using Tevent.Wait on an event set on the same thread, as it’ll block and the event setting will never happen.
The solution I came up with was to wait on the TEvent like I just said I shouldn’t, but only for a short period of time, then call CheckSynchronize to force it to allow the results back from the thread, then wait on the event again, etc, etc up until it either finishes or it times out.
The other key was CheckSynchronize. Before I had that I had a call to Application.ProcessMessages, which apart from making me feel a bit dirty, didn’t actually work.
Reflecting on it now, I possibly don’t even need the tevent, as I suspect I’m just using it as a complicated way to sleep. But if I try refactoring it later I’ll see if it’s actually needed.
So it could probably be tidied up and made easier to reuse, but I’m away. Again, thanks for all your suggestions.
Cheers