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

旧巷老猫 提交于 2019-12-19 05:22:33

问题


I have the following method in an interface..

Task<SearchResult<T>> SearchAsync(TU searchOptions);

works great.

Now i'm trying to make a unit test to test when something goes wrong - and the code throws an exception.

In this case, I've setup my method to throw an HttpRequestException. My unit test fails to say that I threw that exception ..

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

the error message from the unit test is

Shouldly.ChuckedAWobbly
var result = Should
throw
System.Net.Http.HttpRequestException
but does not

So the assertion framework is saying: You've expected an exception, but none was thrown.

When I step -through- the code, the exception is 100% thrown.

Can anyone see what i've done wrong with my unit test code, please?


回答1:


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));



回答2:


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.



回答3:


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.




回答4:


The problem is that the passed lambda returns a Task. The thrown exception can only be observed by Should.Throw if it waits for this task to complete, which apparently it does not. As a work-around, you can .Wait on the task returned by SearchAsync yourself.

mstest (the built-in visual studio testing framework) has support for async tests since Visual Studio 2012. You can basically just change the test method declaration by replacing "void" by "async Task".

[TestMethod]
[ExpectedException(typeof(System.Net.Http.HttpRequestException))]
public async Task SomeTest()
{
   await service.SearchAsync(searchOptions);
}

You are probably using a different unit testing framework, but it's not clear which one. Consult its documentation to see if it supports async tests.

NUnit 2.6.3 also seems to support async tests.

edit: so you are using xUnit. This particular issue was fixed for xUnit 2.0. It's currently still alpha though.




回答5:


The exception is thrown on a different thread to that on which your unit test runs. The unit test framework can only anticipate exceptions on its own thread.

I would suggest you test the exception on a synchronous version of the service.



来源:https://stackoverflow.com/questions/22402701/how-can-i-unit-test-this-async-method-which-correctly-throws-an-exception

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!