An interface that I need to mock declares a Func delegate as a property instead of an event. I am quite used to mocking events by using the SetupAdd(), Raise(), etc. methods of Mock. However, they don't work for a delegate.
The example below shows my situation.
The interface to be mocked for test (third-party, outside my control):
public interface IResource {
Func<string, Response> QuickNotification {get; set;}
}
The class under test:
public class Subject {
private readonly IResource _resource;
Subject(IResource resource) // .ctor
{
_resource = resource;
_resource.QuickNotification += NotificationSink;
}
private Response NotificationSink(string message)
{
// do something..
return new Response();
}
}
Test:
using Moq;
public class SubjectTests {
[Test] public void Test1()
{
var mockResource = new Mock<IResource>();
mockResource.Setup(x => x.QuickNotification).Returns(\\??WHAT TO DO HERE??//);
var sut = new Subject(mockResource.Object);
var response = mockResource.Object.QuickNotification.Invoke("test message");
Assert.IsNotNull(response);
}
}
In Returns()
, I have tried various options ranging from doing SetupAdd()
, intercepting the attached handler in Callback
and finally returning a Mock<Func<string, Response>>::Object
, to returning an actual Func<..,..>
initialized to some value, to many things in between.
However, none of these work. For example, with
var mockFunc = new Mock<Func<string, Response>>();
Func<string, Response> subjectCallback = null;
mockFunc.SetupAdd(_ => _ += It.IsAny<Func<string, Response>>())
.Callback<Func<string, Response>>((cb) => subjectCallback = cb);
...
var response = subjectCallback.Invoke("test message");
in SetupAdd()
, I see the exception:
System.InvalidCastException : Unable to cast object of type 'System.Func`2[..,..]' to type 'Moq.Internals.IProxy'.
The solution turns out be quite simple, but maybe either not well documented or not discovered by me. This works:
[Test] public void Test1()
{
var mockResource = new Mock<IResource>();
mockResource.SetupProperty(x => x.QuickNotification, (Func<string, Response>)null);
var sut = new Subject(mockResource.Object);
var response = mockResource.Object.QuickNotification.Invoke("test message");
Assert.IsNotNull(response);
}
Note the use of SetupProperty
and it returning null.
PS: make sure to not use create the mock as new Mock<IResource>() { DefaultValue = DefaultValue.Mock }
for the above to work. If you do this, Moq sets up the Func property already to return a Mock<Response>
object.