Ayende @ Rahien

Refunds available at head office

Async tests in Silverlight

One of the things that we do is build a lot of stuff in Silverlight, usually, those things are either libraries or UI. Testing Silverlight was always a problem, but at least there is a solution OOTB for that.

Unfortunately, the moment that you start talking about async tests (for example, you want to run a web server to check things), you need to do things like this, EnqueueCallback, EnqueueConditional and other stuff that makes the test nearly impossible to read.

Luckily for us, Christopher Bennage stopped here for a while and created a solution.

It allows you to take the following sync test:

[Fact]
public void CanUpload()
{
    var ms = new MemoryStream();
    var streamWriter = new StreamWriter(ms);
    var expected = new string('a',1024);
    streamWriter.Write(expected);
    streamWriter.Flush();
    ms.Position = 0;

    var client = NewClient(); 
    client.UploadAsync("abc.txt", ms).Wait();

    var downloadString = webClient.DownloadString("/files/abc.txt");
    Assert.Equal(expected, downloadString);
}

And translate it to:

[Asynchronous]
public IEnumerable<Task> CanUpload()
{
    var ms = new MemoryStream();
    var streamWriter = new StreamWriter(ms);
    var expected = new string('a', 1024);
    streamWriter.Write(expected);
    streamWriter.Flush();
    ms.Position = 0;

    yield return client.UploadAsync("abc.txt", ms);

    var async = webClient.DownloadStringTaskAsync("/files/abc.txt");
    yield return async;

    Assert.AreEqual(expected, async.Result);
}

It makes things so much easier. To set this us, just reference the project and add the following in the App.xaml.cs file:

private void Application_Startup(object sender, StartupEventArgs e)
{
    UnitTestSystem.RegisterUnitTestProvider(new RavenCustomProvider());
    RootVisual = UnitTestSystem.CreateTestPage();
}

And you get tests that are now easy to write and run in Silverlight.

Comments

tobi
12/02/2011 01:50 PM by
tobi

This technique is also useful pre C# 5 to get async code emulated using iterators.

José F. Romaniello
12/02/2011 04:20 PM by
José F. Romaniello

I like this thing, it is in fact how you implement co-routines. I guess in the near future with async/await in c# it is going to be simpler.

fact
12/02/2011 09:53 PM by
fact

Silverlight is dead

Harry Steinhilber
12/02/2011 11:53 PM by
Harry Steinhilber

@fact Really? Last time I checked, Microsoft supports products for 10 years. In the next month or two, we are looking at the release of Silverlight 5. That means that Silverlight will be alive and kicking until at least the end of 2021.

Jeff Circeo
12/03/2011 06:00 PM by
Jeff Circeo

Thanks Ayende, this is definitely a much cleaner approach and I love it.

Coreo
12/04/2011 11:50 AM by
Coreo

Harry...

Silverligh IS dead, the technology like that was doomed at the very beginning. It is tedious, prone to errors, runs in a sandbox on the client machine, most corporations would never even allow it to be installed in their networks...Same as for Linq to SQL and other technologies, they just die. Stick to the ones that work. Now we are all bound to move the beaufully colored rectangles on the screen being thrilled that we use Metro....wow. But just for a few years, until Windows Phone finally dies... You wrote this in March this year, saying it is only a month of two until Silverlight 5, now we will soon be in 2012, so where is it? Get real.

lol
12/04/2011 12:25 PM by
lol

No Coreo, he wrote it in December not in March. Get real.

K00lAid
12/04/2011 03:47 PM by
K00lAid

Harry, dude just drink the Kool Aid, dont chug it.

Remco
12/05/2011 01:08 AM by
Remco

@Coreo

March? How can he comment on a post which was not even published then.

Windows Phone dies? Wow, the only one who needs to get real is you. If you look at the world like this, just go sit in a hole somewhere where nobody can iritate you. And nobody is irritated by your useless statements.

my2c

Mouzik
12/05/2011 03:42 PM by
Mouzik

And that is why everyone just needs to write dates yyyy-mm-dd :)

Harry Steinhilber
12/05/2011 11:31 PM by
Harry Steinhilber

@Coreo, And which technologies that work should I stick to then? Maybe I should just continue to write Webforms apps? My point is that Silverlight will be supported for many years to come and there is no harm in continuing to use it.

As far as corporations not installing Silverlight, many actually do. And the fact that it is sandboxed on the client is a feature that corporations like from a security prospective.

As far as where is Silverlight 5, Microsoft released an RC in September, and it was unofficially supposed to RTM in November. However, the official site still shows a 2011 release timeframe. Is that real enough for you?

Peter
12/06/2011 01:02 AM by
Peter

I need to express my feeling here in the comment thread, sorry, here goes:

Who cares if Silverlight dies. Who cares!

I guess it makes testing easier if it does?

Why do we care if Silverlight dies? Why do we care if Windows Phone dies?

It used to matter to me, but I don't understand why now. A lost time investment?

Marwijn
12/06/2011 12:23 PM by
Marwijn

Hi All

I tried to use the same approach using microsoft async CTP to try to make the syntax a bit simpler. I came up with this:

[TestMethod] [Asynchronous] public async Task AnotherWay() { await SomeTestTask.DoSomethingAsync(); int result = await SomeTestTask.DoSomethingAsync();

await Delay(100);

Assert.AreEqual(42, result);

}

To make this working I added

public void ExecuteTest2(MethodInfo test) { Task task = (Task)test.Invoke(this, new object[] { });
EnqueueConditional(()=> task.IsCompleted || task.IsFaulted); EnqueueCallback(() => { if (task.IsFaulted) { throw task.Exception.InnerException; } }); EnqueueTestComplete(); } }

to AsynchronousTaskTest

and

if (Method.ReturnType == typeof(Task)) { var executer =
instance.GetType().GetMethod("ExecuteTest2"); executer.Invoke(instance, new[] { methodInfo }); }

to TestMethod.

Just let me know if it works for you.

Sam
12/07/2011 05:34 AM by
Sam

I wish Silverlight was dead. It's a horrendous CPU hog even when it's (apparently) doing nothing, and it breaks expected UI conventions (context menus bug me the most). I'm pretty sure it's responsible for the random freezes the desktop at home is experiencing.

The only reason I haven't disabled it is Raven Studio and Azure Management Portal, and I dread having to start up either one of those.

Jimmy Zimms
12/07/2011 06:56 PM by
Jimmy Zimms

And reading these comments I see that most everyone missed the entire point here that should be the take away. Regardless of underlying technology, if you are working in an intrinsically async environment (and that should be EVERYONE) then you have to deal with the realities of async APIs and the related tests for them. Oren points out real life techniques and components to handle this problem area in an elegant manner without getting in my way as a developer. Somehow people evolved into Silverlight is Dead vs Long Live Silverlight and missed the boat.

Comments have been closed on this topic.