如何在只有 setter 的接口上模拟委托的提升?
How to mock the raising of a delegate on an interface that only has a setter?
我正在尝试使用 Moq 模拟一个界面,但我不知道该怎么做。界面看起来像这样:
public delegate void MyDelegate(int i);
public interface MyInterface
{
void Method(int i);
MyDelegate MyDelegate { set; }
}
我正在测试一个组件,该组件将具有此接口的对象作为依赖项。我希望能够测试调用方法时引发委托的交互,但我不知道该怎么做。我知道这是一种有点奇怪的界面设计方式,但我无法控制它。
假设我有这样的 class 来测试:
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;
}
}
我试过这样的测试,但在设置模拟时出现异常。 "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);
}
我认为您的方法的根本缺陷是您试图模拟代码中实际未发生的事情 - 即调用 void Method(int i)
随后调用您的委托。您不能使用 Raises
,因为那是针对事件的。
我认为您需要 MyInterface
的 "stub" 实现,您根本不需要使用 Moq。这是我通过断言的代码:
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;
}
}
}
您可以说通过 MyDelegate
局部变量桥接您的 SystemUnderTest.DelegateHandler
。尝试这样的事情:
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));
那么实际上你的 SystemUnderTest.DelegateHandler
方法会在你每次调用 mock.Method
.
时被调用
我正在尝试使用 Moq 模拟一个界面,但我不知道该怎么做。界面看起来像这样:
public delegate void MyDelegate(int i);
public interface MyInterface
{
void Method(int i);
MyDelegate MyDelegate { set; }
}
我正在测试一个组件,该组件将具有此接口的对象作为依赖项。我希望能够测试调用方法时引发委托的交互,但我不知道该怎么做。我知道这是一种有点奇怪的界面设计方式,但我无法控制它。
假设我有这样的 class 来测试:
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;
}
}
我试过这样的测试,但在设置模拟时出现异常。 "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);
}
我认为您的方法的根本缺陷是您试图模拟代码中实际未发生的事情 - 即调用 void Method(int i)
随后调用您的委托。您不能使用 Raises
,因为那是针对事件的。
我认为您需要 MyInterface
的 "stub" 实现,您根本不需要使用 Moq。这是我通过断言的代码:
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;
}
}
}
您可以说通过 MyDelegate
局部变量桥接您的 SystemUnderTest.DelegateHandler
。尝试这样的事情:
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));
那么实际上你的 SystemUnderTest.DelegateHandler
方法会在你每次调用 mock.Method
.