Rhino Mocks, интерфейсы и свойства - PullRequest
0 голосов
/ 28 августа 2009

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

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

Это то, что я пробовал, но это не работает:

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

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

Ответы [ 3 ]

1 голос
/ 28 августа 2009

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

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

Обычным способом сделать то, что вы хотите, было бы изменить конструктор 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 голос
/ 28 августа 2009

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

Итак, вы можете написать такой тест:

[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 голосов
/ 28 августа 2009

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

Например:

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

EDIT: Если подумать, похоже, что Return может быть универсальным методом, что означает, что это может быть дополнительной опцией

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