Async Lambdas

code 0 comments suggest edit

Today I learned something new and I love that!

I was looking at some code that looked like this:

try
{
    await obj.GetSomeAsync();
    Assert.True(false, "SomeException was not thrown");
}
catch (SomeException)
{
}

That’s odd. We’re using xUnit. Why not use the Assert.Throws method? So I tried with the following naïve code.

Assert.Throws<SomeException>(() => await obj.GetSomeAsync());

Well that didn’t work. I got the following helpful compiler error:

error CS4034: The ‘await’ operator can only be used within an async lambda expression. Consider marking this lambda expression with the ‘async’ modifier.

Oh, I never really thought about applying the async keyword to a lambda expression, but it makes total sense. So I tried this:

Assert.Throws<SomeException>(async () => await obj.GetSomeAsync());

Hey, that worked! I rushed off to tell the internets on Twitter.

But I made a big mistake. That only made the compiler happy. It doesn’t actually work. It turns out that Assert.Throws takes in an Action and thus that expression doesn’t return a Task to be awaited upon. Stephen Toub explains the issue in this helpful blog post, Potential pitfalls to avoid when passing around async lambdas.

Ah, I’m gonna need to write my own method that takes in a Func<Task>. Let’s do this!

I wrote the following:

public async static Task<T> ThrowsAsync<T>(Func<Task> testCode)
      where T : Exception
{
  try
  {
    await testCode();
    Assert.Throws<T>(() => { }); // Use xUnit's default behavior.
  }
  catch (T exception)
  {
    return exception;
  }
  // Never reached. Compiler doesn't know Assert.Throws above always throws.
  return null;
}

Here’s an example of a unit test (using xUnit) that makes use of this method.

[Fact]
public async Task RequiresBasicAuthentication()
{
  await ThrowsAsync<SomeException>(async () => await obj.GetSomeAsync());
}

And that works. I mean it actually works. Let me know if you see any bugs with it.

Note that you have to change the return type of the test method (fact) from void to return Task and mark it with the async keyword as well.

So as I was posting all this to Twitter, I learned that Brendan Forster (aka @ShiftKey) already built a library that has this type of assertion. But it wasn’t on NuGet so he’s dead to me.

But he remedied that five minutes later.

Install-Package AssertEx.

So we’re all good again.

If I were you, I’d probably just go use that. I just thought this was an enlightening look at how await works with lambdas.

Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

11 responses

  1. Avatar for Jonas
    Jonas January 23rd, 2013

    Another option would be to call:
    Assert.Throws<someexception>(() => obj.GetSomeAsync().Result);But that will not execute it asynchronous but that may be ok in a testcase right?

  2. Avatar for Michael
    Michael January 24th, 2013

    Very interesting. It sounds almost like there needs to be some sort of async wait: something like Future().Assert.Throws<someexception>(/*my async lambda here*/).

  3. Avatar for Michael
    Michael January 24th, 2013

    'Scuse the Xml tags.

  4. Avatar for calum bett
    calum bett January 24th, 2013

    Bill Wagner recently posted something similar http://www.srtsolutions.com...
    Thought it might be useful.

  5. Avatar for Eric Rodewald
    Eric Rodewald January 24th, 2013

    > ... But it wasn’t on NuGet so he’s dead to me.
    Funniest thing I've read all day. Great!

  6. Avatar for Jayson Knight
    Jayson Knight January 25th, 2013

     Clean and succinct, nice.

  7. Avatar for IlyaGrebnov
    IlyaGrebnov February 7th, 2013

    What you need 'await ' in this statment "ThrowsAsync<someexception>(async () => await obj.GetSomeAsync())". I think ThrowsAsync will take care about it?
     

  8. Avatar for Geza
    Geza February 13th, 2013

     But that will not execute it asynchronous but that may be ok in a testcase right?</someexception>

  9. Avatar for Sergey Teplyakov
    Sergey Teplyakov April 3rd, 2013

    Hi, Phil. Thanks for the great post!

    I think you can slightly simplify your code and remove that "return null" at the bottom of the ThrowsAsync method.

    You can change signature from Task<t> (that is Task<exception> to Task):

    public async static Task ThrowsAsync<t>(Func<task> testCode)
    where T : Exception
    {
    try
    {
    await testCode();
    Assert.Throws<t>(() => { }); // Use xUnit's default behavior.
    }
    catch (T) {}
    }

    The idea is the same, but we actually don't need that exception inside the Task.

  10. Avatar for Rex
    Rex March 17th, 2014

    Can someone please explain the line Assert.Throws<t>(() => { })? Why pass an empty lambda there?

  11. Avatar for Matthew Wilton
    Matthew Wilton November 12th, 2014

    In case people have the same question:

    If the line Assert.Throws<t>(() => { }) is reached it means that testCode has executed without throwing any exceptions, which is a test failure (we are asserting that testCode throws an exception). The line Assert.Throws<t>(() => { }) is there to force the test to fail, the empty lamba will never throw the desired exception.