7

I'm having issues trying to get NSubstitute to return an IEnumerable interface from a Task.

The factory I'm mocking:

public interface IWebApiFactory<T> : IDisposable
{
    <T> GetOne(int id);
    Task<IEnumerable<T>> GetAll();
    Task<IEnumerable<T>> GetMany();
    void SetAuth(string token);
}

The test method:

[TestMethod]
public async Task TestMutlipleUsersAsViewResult()
{
    var employees = new List<EmployeeDTO>()
    {
        new EmployeeDTO(),
        new EmployeeDTO()
    };

    // Arrange
    var factory = Substitute.For<IWebApiFactory<EmployeeDTO>>();
    factory.GetMany().Returns(Task.FromResult(employees));
}

The error I am getting is:

cannot convert from 'System.Threading.Tasks.Task> to System.Func>>

Is this an issue with me passing a list, as a posed to IEnumerable even though List is IEnumerable?

Edit:

These are the functions within NSubstitute

public static ConfiguredCall Returns<T>(this T value, Func<CallInfo, T> returnThis, params Func<CallInfo, T>[] returnThese);
public static ConfiguredCall Returns<T>(this T value, T returnThis, params T[] returnThese);
Nikolay Kostov
  • 16,433
  • 23
  • 85
  • 123
nik0lai
  • 2,585
  • 23
  • 37
  • Notice the error. You are trying to convert a `System.Func` to `Task`. I suspect `Returns` expects a lambda, not a concrete value – Panagiotis Kanavos Feb 19 '15 at 11:40
  • Well I thought this, but I've used it with a single object in a previous test and it works fine. When I pass the list in, it blows up. – nik0lai Feb 19 '15 at 11:46
  • 1
    The overloads expect to find the *same* return type of parameter as the function's. This means that the second overload isn't the best match. Try explicitly casting to `Task>` or `Task.FromResult((IEnumerable)employees)`, or simply declare `IEnumerable employees= ...` – Panagiotis Kanavos Feb 19 '15 at 11:49

1 Answers1

6

None of the overloads is a good match for the value you passed. The second signature, public static ConfiguredCall Returns<T>(this T value, T returnThis, params T[] returnThese); expect a value of the same type as the function's return type so it isn't the best match.

The simplest way to overcome this is to change the declaration of employees to IEnumerable<EmployeeDTO> :

IEnumerable<EmployeeDTO> employees = new List<EmployeeDTO>()
{
    new EmployeeDTO(),
    new EmployeeDTO()
};
Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • Yeah that was it, I assumed that it would detect the inheritance from List to IEnumerable without a cast but with T it has to be strongly typed? Thanks! – nik0lai Feb 19 '15 at 11:55
  • 2
    The behavior you expected is called [covariance](https://msdn.microsoft.com/en-us/library/dd799517%28v=vs.110%29.aspx) and is supported on interface types or delegate parameters. That's why it works with IEnumerable but not with an extension method – Panagiotis Kanavos Feb 19 '15 at 11:58
  • 2
    To be clear, the problem is not the extension method, but the fact that Task is not covariant in T. Thus Task> is not a valid argument type when Task> is expected. – Søren Boisen Jun 26 '15 at 13:43