I am stumped ... This is a follow up post to my previous question on using lambda delegates with Reflection, to perform unit tests on Windows Forms
I got it to work quite nicely and now can use this to confirm certain NFRs.
var monitor = new SemaphoreSlim(1);
monitor.Wait();
var form = new SomeForm();
form.SetControlProperty<TextBox>("tbFirstName", "Text", firstName);
form.SetControlProperty<TextBox>("tbLastName", "Text", lastName);
form.ObserveControlEvents<DataGridView>("dataGridView1", "DataSourceChanged",
(sender, args) =>
{
Trace.WriteLine("Occured in delegate");
monitor.Release();
});
Trace.WriteLine("Executing");
var sw = form.ClickButton("btnSearch");
monitor.Wait();
sw.Stop();
However, if I decorate my search and DataGridView.DataSource update methods, with [BackgroundMethod] and [DispatchedMethod], the tests succeed, but no data is actually inserted in the form under test. In other words, tests complete in 0ms, with 0 records in the DataGridView.
I am using PostSharp 2.1.7.35 and 2.1.1.12 PostSharp Threading Toolkit.
What could be the reason for this behavior?
I've tried the path of creating a UnitTest build configuration and using SkipPostSharp compilation symbol, but it did not feel natural to use and had some other issues.
UPDATE
If I use the Task Factory approach, and perform my UI update on a delegate, it works just fine.
UPDATE 2
It also works if I use BackgroundWorker.
UPDATE 3
Actually, BackgroundWorker did not correctly fire an event on task completion.
So what does PostSharp weave in to brake Unit Tests?
Using this article advice, I have been able to successfully use PostSharp's [BackgroundMethod]
and SafeInvoke
extension from the CodeProject article. Since it has a couple of typos, here is a re-write:
public static TResult SafeInvoke<T,TResult>(this T isi, Func<T,TResult> call) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired) {
IAsyncResult result = isi.BeginInvoke(call, new object[] { isi });
object endResult = isi.EndInvoke(result); return (TResult)endResult;
}
else
return call(isi);
}
public static void SafeInvoke<T>(this T isi, Action<T> call) where T : ISynchronizeInvoke
{
if (isi.InvokeRequired) isi.BeginInvoke(call, new object[] { isi });
else
call(isi);
}
I hope it saves someone some time.