我有一个类,它有一个我需要存根的属性。我不能将它作为构造函数的一部分传递,因为构造它的对象不知道构造函数的参数。
运行单元测试时,我希望能够将属性创建为存根。
这是我尝试过的,但它不起作用:
private DeviceMediator deviceMediator;
private IDeviceControlForm deviceControlForm;
private IDataAccess data;
private ICallMonitor callMonitor;
// Use TestInitialize to run code before running each test
[TestInitialize()]
public void MyTestInitialize()
{
// This line works fine
deviceControlForm = MockRepository.GenerateStub<IDeviceControlForm>();
// This line works fine
data = MockRepository.GenerateStub<IDataAccess>();
// This has to be an ICallMonitor. If I try to make it a
// CallMonitor then it fails.
callMonitor = (CallMonitor)
MockRepository.GenerateStub<ICallMonitor>();
// This line does not compile. Because it wants to
// return a CallMonitor not an ICallMonitor.
Expect.Call(new CallMonitor(null)).Return(callMonitor);
// This is the class that has the CallMonitor (called callMonitor).
deviceMediator = new DeviceMediator(deviceControlForm, data);
}
无论如何都要捕获构造函数对CallMonitor的调用并使其实际成为存根?
如果它是相关的,这是DeviceMediator中的相关代码:
private IDeviceControlForm form;
private readonly IDataAccess data;
public ICallMonitor CallMonitor { get; set; }
public DeviceMediator(IDeviceControlForm form, IDataAccess data)
{
this.form = form;
this.data = data;
CallMonitor = new CallMonitor(OnIncomingCall);
}
提前感谢您的帮助。
答案 0 :(得分:1)
首先,你可以直接在RhinoMock中存根/模拟类,所以如果你想要一个实际的CallMonitor存根而不是ICallMonitor,这将克服代码中的转换问题。转换失败的原因是RhinoMock创建了一个不是CallMonitor的“动态代理”对象。
其次你不能模拟构造函数调用,最重要的是没有办法在DeviceMediator构造函数中模拟对新CallMonitor的调用,因为没有办法注入实例。
通常的做法是将DeviceMediator构造函数更改为:
public DeviceMediator(IDeviceControlForm form, IDataAccess data, ICallMonitor callMonitor) { ... }
然后您的测试可以将此接口的存根/模拟实例注入构造函数。
编辑:如果你真的无法将一个实例注入构造函数,那么你有几个选择:
创建一个可以存根的工厂:
public class CallMonitorFactory { public virtual CallMonitor CreateMonitor(args...) { } } public DeviceMediator(IDeviceControlForm form, IDataAccess data, CallMonitorFactory factory) { this.form = form; this.data = data; CallMonitor = factory.CreateMonitor(OnIncomingCall); }
在DeviceMediator上添加受保护的工厂方法,该方法返回CallMonitor。然后,您必须在测试中手动创建DeviceMediator的子类,以便返回模拟CallMonitor对象。
将CallMonitor的构造函数参数移动到DeviceMediator构造函数中调用的方法/属性中。
您似乎正在尝试在CallMonitor上侦听某种事件,因此您可以(并且如果是这种情况)添加DeviceMediator订阅的事件。在这种情况下,您可以使用RhinoMock模拟事件引发调用,如下所示:
[Test] public void IncomingCallTest() { IEventRaiser callEvent; CallMonitor monitor = mocks.Stub(args..); using(mocks.Record()) { callEvent = monitor.Stub(m => m.IncomingCall += null).IgnoreArguments().GetEventRaiser(); //rest of expectations... } using(mocks.Playback()) { DeviceMediator mediator = new DeviceMediator(form, data, monitor); callEvent.Raise(sender, args); } }
但是,如上所述,你不能使用RhinoMock模拟构造函数调用,因为这需要对生成的IL进行一些更改(假设它甚至可能)。
答案 1 :(得分:1)
由于CallMonitor属性是可写的,您只需使用模拟实例覆盖原始值(您的DeviceMediator实际上实现了属性注入设计模式)。
所以你可以这样写一个测试:
[TestMethod]
public void MyTest()
{
var deviceControlForm = MockRepository.GenerateStub<IDeviceControlForm>();
var data = MockRepository.GenerateStub<IDataAccess>();
var mockCallMonitor = MockRepository.GenerateStub<ICallMonitor>();
var deviceMediator = new DeviceMediator(deviceControlForm, data);
deviceMediator.CallMonitor = mockCallMonitor;
// The rest of the test...
}
答案 2 :(得分:0)
我对Rhino没有太多经验,但你是否尝试在调用Return时将callMonitor转换为CallMonitor?
例如:
Expect.Call(new CallMonitor(null)).Return((CallMonitor)callMonitor);
编辑: 再想一想,看起来Return可能是一种通用方法,这意味着这可能是一个额外的选择
Expect.Call(new CallMonitor(null)).Return<CallMonitor>(callMonitor);