1

I have a service that is implemented in a separate library and is taking a Func<T> as a parameter of a function.

When mocking the service I still need my Func<T> to be run.

How do I setup the mock object for this?

I have created the following code example to demonstrate the problem:

// This is the service that is in a different library and I want to mock
public interface IServiceToMock
{
    T ExecuteAsync<T>(Func<T> func);
}

public class ServiceToMock : IServiceToMock
{
    public T ExecuteAsync<T>(Func<T> funcToBeExecuted)
    {
        // does some more logic that I don't want to test
        return funcToBeExecuted();
    }
}

This is the consumer of the service

public class ServiceConsumer
{
    private readonly IServiceToMock service;

    public ServiceConsumer(IServiceToMock service) => this.service = service;

    public async Task Consume()
    {
        await service.ExecuteAsync(async () => await ConsumeInteger(1));
        await service.ExecuteAsync(async () => await ConsumeString("String"));
    }

    // This is the method that I want to be triggered when the Consume() is called
    private async Task ConsumeInteger(int number)
    {
        await new Task<int>(() =>
        {
            Console.WriteLine(number);
            return number;
        });
    }

    // This is the method that I want to be triggered when the Consume() is called
    private async Task ConsumeString(string str)
    {
        await new Task<string>(() =>
        {
            Console.WriteLine(str);
            return str;
        });
    }
}

This is the test I have so far

public class TestServiceConsumer
{
    public void TestMethod()
    {
        var moqLibraryService = new Mock<IServiceToMock>();
        // How can I set up the mock to have the Console 
        moqLibraryService.Setup(ms => ms.ExecuteAsync(It.IsAny<Func<T>>())).Returns(T());
    }
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Dan
  • 11
  • 1

1 Answers1

1

I think you could do this:

var moqLibraryService = new Mock<IServiceToMock>();

moqLibraryService.Setup(ms => ms.ExecuteAsync(It.IsAny<Func<int>>()))
  .Returns( (Func<int> f) => f() );
moqLibraryService.Setup(ms => ms.ExecuteAsync(It.IsAny<Func<string>>()))
  .Returns( (Func<string> f) => f() );

It uses an overload of Returns which takes in a valueFunction rather than just a value. Each time the mocking is done, that anonymous function (Func<int> f) => f() will be evaluated again. Here f is the it-is-any argument passed to Moq.

Maybe it is harder to do if you do not want to mention the types (here int, string) explicitly. That would be related to the thread Mocking generic methods in Moq without specifying T.

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181
  • That is a good idea, but the acttually service is a bit more so the simple way to do to use a stub. – Dan Jan 11 '19 at 10:51