简体   繁体   中英

Moq: Invalid callback. Setup on method with parameters cannot invoke callback with parameters not using Callback

Interface:

Task<ServiceResponse<string>> GetJSON<T>(FileRequest request, FileItemsSerializer<T> serializer = null, CsvConfiguration configuration = null, ClassMap<T> mapper = null) where T: class, new();    

Moq Setup:

 Mock<IAdFileService> mock = new Mock<IAdFileService>();
     
mock.Setup(x => x.GetJSON(
                        It.IsAny<FileRequest>(), 
                        It.IsAny<FileItemsSerializer<dynamic>>(),
                        It.IsAny<CsvConfiguration>(),
                        It.IsAny<ClassMap<dynamic>>()
                    )
            ).Returns<ServiceResponse<string>>(
                (a) => { 
                    return Task.FromResult(ServiceResponse<string>.Create("Json Data", "http://test.com/", "Json Data", "http://test.com/")); 
                });

Error message is

System.ArgumentException HResult=0x80070057 Message=Invalid callback. Setup on method with 4 parameter(s) cannot invoke callback with different number of parameters (1). Source=Moq StackTrace:
at Moq.MethodCall.<>c__DisplayClass22_0.g__ValidateCallback|4(Delegate callback) in C:\\projects\\moq4\\src\\Moq\\MethodCall.cs:line 311 at Moq.MethodCall.SetReturnComputedValueBehavior(Delegate valueFactory) in C:\\projects\\moq4\\src\\Moq\\MethodCall.cs:line 256 at Moq.Language.Flow.NonVoidSetupPhrase 2.Returns[T1](Func 2 valueExpression) in C:\\projects\\moq4\\src\\Moq\\Language\\Flow\\NonVoidSetupPhrase.cs:line 281

I would like to use

 mock.Setup(x => x.GetJSON<dynamic>(It.IsAny<FileRequest>())
            ).Returns<ServiceResponse<string>>(
                (a) => { 
                    return Task.FromResult(ServiceResponse<string>.Create("Json Data", "http://test.com/", "Json Data", "http://test.com/")); 
                });

Since the last 3 parameters on getJSON are defaulted to null.

My question is: Why does not work and returns the error message. What am I doing wrong? I attempted to set it up similar to
Moq: Invalid callback. Setup on method with parameters cannot invoke callback with parameters

Thank you

Your code isn't working because you are using the Returns overload that allows you to get hold of the parameters provided to the invocation, but you're not providing the type and you're not providing all of them. It's not about them having default values, it's that you're not providing the definition that Moq expects.

There's probably a few ways you can cut this. Given:

var adFileServiceMock = new Mock<IAdFileService>();
var expectedMockResponse = ServiceResponse<string>.Create("Json Data", "http://test.com/", "Json Data", "http://test.com/");
  1. Always return that specific instance:
adFileServiceMock
   .Setup(x => x.GetJSON<It.IsAnyType>(It.IsAny<FileRequest>(), It.IsAny<FileItemsSerializer<It.IsAnyType>>(), It.IsAny<CsvConfiguration>(), It.IsAny<ClassMap<It.IsAnyType>>()))
   .Returns(Task.FromResult(expectedMockResponse));
  1. Use a factory, (in this case I'm just returning the same instance everytime)
adFileServiceMock
   .Setup(x => x.GetJSON<It.IsAnyType>(It.IsAny<FileRequest>(), It.IsAny<FileItemsSerializer<It.IsAnyType>>(), It.IsAny<CsvConfiguration>(), It.IsAny<ClassMap<It.IsAnyType>>()))
   .Returns(() => Task.FromResult(expectedMockResponse));

This is what I normally do.

  1. Specify the parameters
adFileServiceMock
   .Setup(x => x.GetJSON<It.IsAnyType>(It.IsAny<FileRequest>(), It.IsAny<FileItemsSerializer<It.IsAnyType>>(), It.IsAny<CsvConfiguration>(), It.IsAny<ClassMap<It.IsAnyType>>()))
   .Returns((FileRequest providedFileRequest, object providedFileItemsSerializer, CsvConfiguration providedCsvConfiguration, object providedClassMap) => Task.FromResult(expectedMockResponse));

I do this when what I return depends on the values/objects provided to the invocation.

You've got to spec the types. I've been lazy here for brevity and used It.IsAnyType and object . I would normally use a specific type or a generic type parameter.

Finally given you're returning a task, at a guess you're using an async process so consider using ReturnsAsync

adFileServiceMock
   .Setup(x => x.GetJSON<It.IsAnyType>(It.IsAny<FileRequest>(), It.IsAny<FileItemsSerializer<It.IsAnyType>>(), It.IsAny<CsvConfiguration>(), It.IsAny<ClassMap<It.IsAnyType>>()))
   .ReturnsAsync(() => expectedMockResponse);

Working LINQPad example:

async void Main()
{
    var adFileServiceMock = new Mock<IAdFileService>();
    var expectedMockResponse = ServiceResponse<string>.Create("Json Data", "http://test.com/", "Json Data", "http://test.com/");

//  adFileServiceMock
//      .Setup(x => x.GetJSON<It.IsAnyType>(It.IsAny<FileRequest>(), It.IsAny<FileItemsSerializer<It.IsAnyType>>(), It.IsAny<CsvConfiguration>(), It.IsAny<ClassMap<It.IsAnyType>>()))
//      .Returns(Task.FromResult(expectedMockResponse));
//
//  adFileServiceMock
//      .Setup(x => x.GetJSON<It.IsAnyType>(It.IsAny<FileRequest>(), It.IsAny<FileItemsSerializer<It.IsAnyType>>(), It.IsAny<CsvConfiguration>(), It.IsAny<ClassMap<It.IsAnyType>>()))
//      .Returns(() => Task.FromResult(expectedMockResponse));
//
//  adFileServiceMock
//      .Setup(x => x.GetJSON<It.IsAnyType>(It.IsAny<FileRequest>(), It.IsAny<FileItemsSerializer<It.IsAnyType>>(), It.IsAny<CsvConfiguration>(), It.IsAny<ClassMap<It.IsAnyType>>()))
//      .Returns((FileRequest providedFileRequest, object providedFileItemsSerializer, CsvConfiguration providedCsvConfiguration, object providedClassMap) => Task.FromResult(expectedMockResponse));

    adFileServiceMock
        .Setup(x => x.GetJSON<It.IsAnyType>(It.IsAny<FileRequest>(), It.IsAny<FileItemsSerializer<It.IsAnyType>>(), It.IsAny<CsvConfiguration>(), It.IsAny<ClassMap<It.IsAnyType>>()))
        .ReturnsAsync(() => expectedMockResponse);

    var adFileService = adFileServiceMock.Object;

    var mockResponse = await adFileService.GetJSON(new FileRequest(), new FileItemsSerializer<Foo>(), new CsvConfiguration(), new ClassMap<Foo>());

    mockResponse.Should().BeSameAs(expectedMockResponse);
}

// You can define other methods, fields, classes and namespaces here
public interface IAdFileService
{
    Task<ServiceResponse<string>> GetJSON<T>(FileRequest request, FileItemsSerializer<T> serializer = null, CsvConfiguration configuration = null, ClassMap<T> mapper = null) where T : class, new();
}

public class ServiceResponse<T>
{
    public static ServiceResponse<T> Create(string a, string b, string c, string d)
    {
        return new ServiceResponse<T>();
    }
}

public class FileRequest { }

public class FileItemsSerializer<T> { }

public class CsvConfiguration { }

public class ClassMap<T> { }

public class Foo { }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM