How can I unit test this async method which (correctly) throws an exception?

The problem is that your assertion framework does not understand asynchronous methods. I recommend you raise an issue with them.

In the meantime, you can use the source for Should.Throw to write your own MyShould.ThrowAsync:

public static async Task<TException> ThrowAsync<TException>(Func<Task> actual)
    where TException : Exception
{
  try
  {
    await actual();
  }
  catch (TException e)
  {
    return e;
  }
  catch (Exception e)
  {
    throw new ChuckedAWobbly(new ShouldlyMessage(typeof(TException), e.GetType()).ToString());
  }

  throw new ChuckedAWobbly(new ShouldlyMessage(typeof(TException)).ToString());
}

And use it as such:

var result = await MyShould.ThrowAsync<HttpRequestException>
    (async () => await service.SearchAsync(searchOptions));

or the slightly simpler and equivalent:

var result = await MyShould.ThrowAsync<HttpRequestException>
    (() => service.SearchAsync(searchOptions));

Unit testing async code/functionality is pretty hard. I myself am getting into unit testing async and running into the same problems as you do.

I found the following two resources very helpful:

  • Best practices in async programming - it delves into the subject of async and the problems with testing it.
  • Unit testing async the wrong way and Unit testing async the right way - delves into the subject, shows the problems that you will encounter and how to setup the testing.

Test it like this:

var result = Should.Throw<HttpRequestException>
    (() => service.SearchAsync(searchOptions).Result);

Or:

var result = Should.Throw<HttpRequestException>
    (() => service.SearchAsync(searchOptions).Wait());

Otherwise, your Should.Throw returns before the async lambda has completed.