2009-08-28 4 views
0

У меня есть класс, который имеет свойство, которое мне нужно заглушить. Я не могу передать его как часть конструктора, потому что объект, строящий его, не знает параметры конструктора.Rhino Mocks, Интерфейсы и свойства

При выполнении модульных тестов я хочу, чтобы свойство было создано как заглушка.

Это то, что я пытался, но он не работает:

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); 
} 

Заранее спасибо за любую помощь.

ответ

1

Поскольку свойство CallMonitor доступно для записи, вы можете просто перезаписать исходное значение экземпляром mock (ваш DeviceMediator фактически реализует шаблон проектирования Injection Property).

Таким образом, вы можете написать тест, как это:

[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... 
} 
+0

Я думаю, что это путь. (Я могу переместить конструктор callMonitor для вызова другого метода.) – Vaccano

0

У меня не слишком много опыта с Rhino, в частности, но вы попробовали лить callMonitor CallMonitor в призыве к возвращению?

Например:

Expect.Call(new CallMonitor(null)).Return((CallMonitor)callMonitor); 

EDIT: На второй мысли, похоже, возврат может быть общий метод, который означает, что это может быть дополнительным вариантом

Expect.Call(new CallMonitor(null)).Return<CallMonitor>(callMonitor); 
+0

Это проливает следующее исключение: System.InvalidOperationException – Vaccano

1

Во-первых, вы можете окурок/mock непосредственно в RhinoMock, поэтому, если вы хотите, чтобы фактический контур CallMonitor, а не ICallMonitor, вы можете, и это преодолеет проблему каста в вашем коде. Причиной отказа от сбоя является то, что RhinoMock создает объект «динамический прокси», который не является CallMonitor.

Во-вторых, вы не можете издеваться над конструкторскими вызовами, и, самое главное, не существует способа издеваться над вызовом нового CallMonitor в конструкторе DeviceMediator, поскольку нет возможности вставить экземпляр.

Обычный способ сделать то, что вы хотите, было бы изменить конструктор DeviceMediator к этому:

public DeviceMediator(IDeviceControlForm form, IDataAccess data, ICallMonitor callMonitor) { ... } 

Тогда ваш тест может придать незавершенная/макет экземпляр этого интерфейса в конструктор.

EDIT: Если вы действительно не можете вводить экземпляр в конструктор, то у вас есть несколько вариантов:

Создать завод, который вы можете STUB:

 
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 в своем тесте, чтобы вы могли вернуть объект Mock 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 (при условии, что это даже возможное).

+0

Как я уже говорил в моем вопросе мне нужно не пройти ICallMonitor как часть конструктора. (Я уже сделал это с другими моими интерфейсами, которые могут быть переданы.) – Vaccano