NSubstitute - mock throwing an exception in method returning Task
Asked Answered
T

5

38

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?

Tompkins answered 12/7, 2016 at 21:7 Comment(0)
L
47

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.

Lacrimator answered 25/4, 2018 at 16:25 Comment(5)
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.Lacrimator
Task.FromException<List<object>> List<object> type is important, or there will be CouldNotSetReturnDueToTypeMismatchExceptionCozen
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);Bradbradan
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 Task1 for IOrdersResource.SubmitAsync (expected type Task1).Gyromagnetic
I've got an error when I try to do this. Cannot convert from 'System.Threading.Tasks.Task<System.Collections.Generic.List<object>>' to 'System.Func<NSubstitute.Core.CallInfo, System.Threading.Tasks.Task<System.Guid>Goldner
T
31

This worked:

using NSubstitute.ExceptionExtensions;

myService.GetAllAsync().Throws(new Exception());
Tompkins answered 12/7, 2016 at 21:7 Comment(5)
This is a synchronous exception, see my answer for the correct way.Lacrimator
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.Significancy
This is incorrect. It does not simulate what would happen in a real world scenario. Do this insteadPrecipitancy
@Precipitancy if really want to simulate a real scenario do integration test, for the unit test it works perfectly.Goldner
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.Sinotibetan
P
5

This is what worked for me:

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

private List<object> ThrowException()
{
        throw new Exception();
}
Particularize answered 22/1, 2018 at 9:2 Comment(0)
S
1

This simple line worked for me:

Assert.ThrowsAsync<Exception>(async () => await _service.MyAsyncFunction());
Software answered 9/8, 2023 at 0:7 Comment(0)
L
0

in my case it worked as follows

_substiture.GetAsync<SomeType?>("").ThrowsAsyncForAnyArgs(new CustomException(System.Net.HttpStatusCode.InternalServerError, "some message"));
Laudanum answered 22/9, 2022 at 14:13 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.