views:

464

answers:

3

I have a class that has a property that I need to stub. I can't pass it as part of the constructor because the object constructing it does not know the parameters of the constructor.

When running unit tests, I want to be able to have the property be created as a stub.

This is what I have tried, but it does not work:

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

Is there anyway to catch the constructor call to CallMonitor and make it actually be a stub?

In case it is relevant, here is the related code in 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);
 }

Thanks in advance for any help.

A: 

I do not have too much experience with Rhino in particular, but did you try casting the callMonitor to a CallMonitor in the call to Return?

For example:

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

EDIT: On second thought, it looks like Return might be a generic method, which means this could be an additional option

Expect.Call(new CallMonitor(null)).Return<CallMonitor>(callMonitor);
Guvante
This throws the following exception: System.InvalidOperationException
Vaccano
+1  A: 

Firstly you can stub/mock classes directly in RhinoMock so if you want an actual CallMonitor stub rather than ICallMonitor you can, and this will overcome the casting issue in your code. The reason the cast fails is that RhinoMock creates a 'dynamic proxy' object which is not CallMonitor.

Secondly you cannot mock constructor calls, and most importantly there is no way to mock the call to new CallMonitor in the DeviceMediator constructor since there is no way to inject an instance.

The usual way to do what you want would be to change the DeviceMediator constructor to this:

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

Then your test can inject a stub/mock instance of this interface into the constructor.

EDIT: If you really can't inject an instance into the constructor then you have a few options:

Create a factory which you can 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);
}
Add a protected factory method on DeviceMediator which returns a CallMonitor. You will then have to manually create a sub-class of DeviceMediator in your test so you can return the mock CallMonitor object. Move the constructor argument for CallMonitor into a method/property that is called in the DeviceMediator constructor. It appears you're trying to listen for an event of some kind on the CallMonitor, so you could (and should if this is the case) add an event which the DeviceMediator subscribes to. In this case you can use RhinoMock to mock the event raising call like this:
[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);
    }
}

However, as noted above, you cannot mock constructor calls using RhinoMock since this would require some changes to the generated IL (assuming it's even possible).

Lee
As I said in my question I need to not pass the ICallMonitor as part of the constructor. (I have already done this with my other interfaces that can be passed in.)
Vaccano
+1  A: 

Since the CallMonitor property is writable, you can just overwrite the original value with a mock instance (your DeviceMediator actually implements the Property Injection design pattern).

So you can write a test like this:

[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...
}
Mark Seemann
I think this is the way to go. (I may move the callMonitor constructor call out to a separate method too.)
Vaccano