Having been reading some Stephen Cleary I found this post:
http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
With this example:
// My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
using (var client = new HttpClient())
{
var jsonString = await client.GetStringAsync(uri);
return JObject.Parse(jsonString);
}
}
// My "top-level" method.
public async void Button1_Click(...)
{
var json = await GetJsonAsync(...);
textBox1.Text = json;
}
How would one turn this code into an NUnit unit test to demonstrate the same solution to the same problem?
I have this code as a unit test but it does not behave the same as the interface example code - unless I am misunderstanding the piece.
// My "library" method.
public static async Task<JObject> GetJsonAsync(Uri uri)
{
using (var client = new HttpClient())
{
var jsonString = await client.GetStringAsync(uri);
return JObject.Parse(jsonString);
}
}
// My "top-level" method.
[Test]
public async void TestButton1_Click(...)
{
var json = await GetJsonAsync(...);
var gotOutput = json;
}
There is no assertion as that will come later.
There are two components to that deadlock: blocking on asynchronous code, and a single-threaded context.
The code sample you pasted does not block on asynchronous code (no .Result
). It looks like you copied the sample that does not deadlock.
To reproduce the deadlock, you'd need to both block on the asynchronous code, and supply a single-threaded context. NUnit does have a single-threaded context that it applies in some scenarios. The details have changed a few times, but I'm pretty sure they ended up applying a context to async void
methods, so I think this would deadlock:
[Test]
public async void TestButton1_Click(...)
{
var json = GetJsonAsync(...).Result;
}
You can check to see if NUnit is supplying a context by reading SynchronizationContext.Current
from a breakpoint in your test. If it's not, then you can supply a context yourself like the AsyncContext
in my AsyncEx
library:
[Test]
public void TestButton1_Click(...)
{
var json = AsyncContext.Run(() => GetJsonAsync(...).Result);
}