Friday, October 15, 2010

Asynchronous Silverlight Unit Tests

So, after beginning to do some unit testing of View Models in Silverlight, I discovered that there have not been that many articles written about how to do this. Fortunately, I did find http://www.jeff.wilcox.name/2009/03/asynchronous-testing/ but other than that, most of what I have learned has been by trial and error. Therefore, here are my findings. Hopefully this will help someone else.


1. "Asynchronous" is a bit of a misnomer. Think of all of the "Enqueue" commands as "Do this later". In traditional asynchronous processing, if you fire off something like an "EnqueueConditional" command, it will fire off a new thread that will begin executing immediately. This is not the case with the Silverlight unit tests. Here is what I did as an example:


[TestMethod]
[Asynchronous]
[Description("Whatever")]
public void TestMethod()
{
int x = 0;
EnqueueConditional(() => x == 2);
EnqueueCallback(() => { method1(); });
x = 1;
EnqueueConditional(() => x == 1);
EnqueueCallback(() => { method2(); });
x = 2;
EnqueueTestComplete();
}


Now, what I would expect would be that method2 would be called before method1. After all, x == 1 as soon as the 2nd condition is enqueued. However, as I said earlier "Enqueue" should be thought of as "do this later". Therefore, what actually will happen is that method1 will be called (because at the end of the method's synchronous processing x == 2), and method2 will never be called (because x != 1 ever again in the method - also, this will never get to the TestComplete, and so will be an infinite test, just so you're warned).



2. "EnqueueConditional" basically means "wait until". This seems to be the most important piece of the processing. This is the key that will allow you to ensure that a condition is met. After this happens, all of the EnqueueCallbacks (including EnqueueTestComplete) are executed until the new "EnqueueConditional", which will then perform another wait until the condition is completed.


3. "EnqueueCallback" just allows code to be done later. The "later" is driven based off of the EnqueueConditionals. Consider the following code snippet:


[TestMethod]
[Asynchronous]
[Description("TestMethod2")]
public void TestMethod2()
{
int x = 0;
EnqueueCallback(() => x = 2);
x = 1;
EnqueueConditional(() => x == 2);
EnqueueTestComplete();
}


In this situation, the EnqueueCallback has nothing that it has to wait on, but since it is an "EnqueueCallback", it will not execute until the end of the method. It will function the same as if I had written the code this way:


[TestMethod]
[Asynchronous]
[Description("TestMethod2")]
public void TestMethod2()
{
int x = 0;
x = 1;
EnqueueConditional(() => x == 2);
EnqueueTestComplete();
x = 2;
}