36

Using NSubstitute, how do you mock an exception being thrown in a method returning a Task?

Let's say our method signature looks something like this:

Task<List<object>> GetAllAsync();

Here's how NSubstitute docs say to mock throwing exceptions for non-void return types. But this doesn't compile :(

myService.GetAllAsync().Returns(x => { throw new Exception(); });

So how do you accomplish this?

Jim Aho
  • 9,932
  • 15
  • 56
  • 87
Brandon
  • 1,566
  • 2
  • 14
  • 22

5 Answers5

44

Actually, the accepted answer mocks a synchronous exception being thrown, that is not the real async behavior. The correct way to mock is:

var myService = Substitute.For<IMyService>();
myService.GetAllAsync()
         .Returns(Task.FromException<List<object>>(new Exception("some error")));

Let's say you had this code and GetAllAsync()

try
{
    var result = myService.GetAllAsync().Result;
    return result;
}
catch (AggregateException ex)
{
    // whatever, do something here
}

The catch would only be executed with Returns(Task.FromException>(), not with the accepted answer since it synchronously throws the exception.

dstj
  • 4,800
  • 2
  • 39
  • 61
  • No, try it with real code (not mocks), if your `async` method throws `MyCustomException` for example, an `AggregationException` will be received if you use `.Result`. If you use `.GetAwaiter().GetResult()`, then you'll get the real `MyCustomException`. Your way would always throws `MyCustomException`, regardless of how it's being used. – dstj Apr 26 '18 at 02:14
  • 1
    Task.FromException> List type is important, or there will be CouldNotSetReturnDueToTypeMismatchException – Carlos Liu Dec 03 '18 at 07:21
  • This is the real correct answer if you want to catch the exception, for example , in a ContinueWith (only on faulted). fileProcessor.ProcessAsync(path, ct).ContinueWith(task => { AggregateException flattened = task?.Exception?.Flatten(); flattened?.Handle(ex => { Log.Error("Error on the file processing : {exception}", ex.ToString()); return true; }); }, TaskContinuationOptions.OnlyOnFaulted); – gatsby Dec 13 '18 at 13:38
  • I get an error when I try to do this. Looks like NSubstitute doesn't like that I'm returning a type (error) that doesn't match my return (success response). Maybe this was introduced recently. Any suggestions? NSubstitute.Exceptions.CouldNotSetReturnDueToTypeMismatchException : Can not return value of type Task`1 for IOrdersResource.SubmitAsync (expected type Task`1). – Crhistian Ramirez Feb 02 '21 at 16:04
  • 1
    I've got an error when I try to do this. Cannot convert from 'System.Threading.Tasks.Task>' to 'System.Func – Pedro S Cord Mar 25 '21 at 16:26
30

This worked:

using NSubstitute.ExceptionExtensions;

myService.GetAllAsync().Throws(new Exception());
Brandon
  • 1,566
  • 2
  • 14
  • 22
  • 7
    This is a synchronous exception, see my answer for the correct way. – dstj Apr 25 '18 at 19:48
  • 6
    The biggest problem with this is that the exception is thrown at the wrong time. `var t = AsyncMethod(); await t;`. With an async method the exception is thrown when awaiting the task, your approach throws the exception immediately. What kind of exception is thrown depends on a lot of things. – Voo Apr 14 '19 at 10:54
  • 1
    This is incorrect. It does not simulate what would happen in a real world scenario. Do [this instead](https://stackoverflow.com/a/50027096/542251) – Liam Jul 14 '20 at 10:52
  • @Liam if really want to simulate a real scenario do integration test, for the unit test it works perfectly. – Pedro S Cord Mar 25 '21 at 17:13
  • As other mentions, this doesn't work when you are doing stuff with the task, such as `ContinueWith`. However, it will most likely give the same result if you're simply awaiting the task. – smoksnes Oct 25 '21 at 13:09
4

This is what worked for me:

myService.GetAllAsync().Returns(Task.Run(() => ThrowException()));

private List<object> ThrowException()
{
        throw new Exception();
}
0

in my case it worked as follows

_substiture.GetAsync<SomeType?>("").ThrowsAsyncForAnyArgs(new CustomException(System.Net.HttpStatusCode.InternalServerError, "some message"));
SIbghat
  • 281
  • 3
  • 5
0

This simple line worked for me:

Assert.ThrowsAsync<Exception>(async () => await _service.MyAsyncFunction());
John
  • 3
  • 5