Reputation: 25310
I'm trying to mock an interface using Moq, but I can't see how to do it. The interface looks something like this:
public delegate void MyDelegate(int i);
public interface MyInterface
{
void Method(int i);
MyDelegate MyDelegate { set; }
}
I am testing a component that takes an object with this interface as a dependency. I would like to be able to test an interaction where the delegate is raised when the method is called, but I can't see how to do it. I know this is a slightly strange way to design an interface, but I don't have control over that.
Suppose I have a class like this to test:
class SystemUnderTest
{
int i = 0;
readonly MyInterface myInterface;
public SystemUnderTest(MyInterface myInterface)
{
this.myInterface = myInterface;
this.myInterface.MyDelegate = DelegateHandler;
}
public int Run(int input)
{
this.myInterface.Method(input);
return i;
}
void DelegateHandler(int i)
{
this.i = i;
}
}
I've tried a test like this, but it gives an exception when setting up the mock. "ArgumentException: Could not locate event for attach or detach method Void set_MyDelegate(ConsoleApp1.MyDelegate)."
static void Main(string[] args)
{
// Arrange
Mock<MyInterface> mock = new Mock<MyInterface>();
mock
.Setup(m => m.Method(It.IsAny<int>()))
.Raises(m => m.MyDelegate = null, 5);
// Act
var sut = new SystemUnderTest(mock.Object);
var result = sut.Run(5);
// Assert
Trace.Assert(result == 5);
}
Upvotes: 3
Views: 794
Reputation: 9519
You could so to say bridge your SystemUnderTest.DelegateHandler
through a MyDelegate
local variable. Try something like this:
MyDelegate del = null;
var mock = new Mock<MyInterface>();
mock.SetupSet(m => m.MyDelegate = It.IsAny<MyDelegate>())
.Callback<MyDelegate>(d => del = d);
mock.Setup(m => m.Method(It.IsAny<int>()))
.Callback<int>(i => del.Invoke(i));
Then actually your SystemUnderTest.DelegateHandler
method will be called every time you invoke mock.Method
.
Upvotes: 6
Reputation: 6713
I think the fundamental flaw in your approach is you're trying to mock something that doesn't actually occur in your code - namely that calling the void Method(int i)
subsequently calls your delegate. You can't use Raises
as that's for events.
I think you need a "stub" implementation of MyInterface
, and you don't need to use Moq at all. Here's my code that passes the assertion:
using System.Diagnostics;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
// Arrange
var stub = new StubOfMyInterface();
// Act
var sut = new SystemUnderTest(stub);
var result = sut.Run(5);
// Assert
Trace.Assert(result == 5);
}
}
public delegate void MyDelegate(int i);
public interface MyInterface
{
void Method(int i);
MyDelegate MyDelegate { set; }
}
public class StubOfMyInterface : MyInterface
{
public void Method(int i)
{
MyDelegate?.Invoke(i);
}
public MyDelegate MyDelegate { get; set; }
}
class SystemUnderTest
{
int i = 0;
readonly MyInterface myInterface;
public SystemUnderTest(MyInterface myInterface)
{
this.myInterface = myInterface;
this.myInterface.MyDelegate = DelegateHandler;
}
public int Run(int input)
{
this.myInterface.Method(input);
return i;
}
void DelegateHandler(int i)
{
this.i = i;
}
}
}
Upvotes: 2