2009-08-28 9 views
0

J'ai une classe qui a une propriété que j'ai besoin de talonner. Je ne peux pas le passer en tant que partie du constructeur parce que l'objet qui le construit ne connaît pas les paramètres du constructeur.Rhino Mocks, Interfaces et propriétés

Lors de l'exécution de tests unitaires, je souhaite que la propriété soit créée en tant que stub.

C'est ce que je l'ai essayé, mais ça ne fonctionne pas:

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

Y at-il de toute façon d'attraper l'appel du constructeur à CallMonitor et en faire soit en réalité un bout?

Dans le cas où il est pertinent, voici le code lié à 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); 
} 

Merci à l'avance pour toute aide.

Répondre

1

Étant donné que la propriété CallMonitor est accessible en écriture, vous pouvez remplacer la valeur d'origine par une instance simulée (votre DeviceMediator implémente réellement le modèle de conception Injection de propriété).

Vous pouvez donc écrire un test comme celui-ci:

[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

Je pense que c'est la voie à suivre. (Je peux également déplacer le constructeur callMonitor dans une autre méthode.) – Vaccano

0

Je n'ai pas trop d'expérience avec Rhino en particulier, mais avez-vous essayé de lancer callMonitor dans un CallMonitor dans l'appel de Return?

Par exemple:

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

EDIT: En y repensant, il semble que de retour pourrait être une méthode générique, ce qui signifie que cela pourrait être une option supplémentaire

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

Cela jette l'exception suivante: System.InvalidOperationException – Vaccano

1

vous pouvez d'abord bouchonner/simulez des classes directement dans RhinoMock, donc si vous voulez un talon CallMonitor réel plutôt que ICallMonitor vous le pouvez, et cela permettra de résoudre le problème d'incantation dans votre code. La raison pour laquelle la distribution échoue est que RhinoMock crée un objet 'proxy dynamique' qui n'est pas CallMonitor.

Deuxièmement, vous ne pouvez pas simuler les appels constructeurs, et surtout, il n'y a aucun moyen de se moquer de l'appel à nouveau CallMonitor dans le constructeur DeviceMediator car il n'y a aucun moyen d'injecter une instance.

La manière habituelle de faire ce que vous voulez serait de changer le constructeur DeviceMediator à ceci:

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

Ensuite, votre test peut injecter un talon/instance simulée de cette interface dans le constructeur.

EDIT: Si vous ne pouvez vraiment pas injecter une instance dans le constructeur, vous avez quelques options:

Créer une usine que vous pouvez 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); 
}

Ajouter une méthode de fabrication protégée sur DeviceMediator qui renvoie un CallMonitor. Vous devrez ensuite créer manuellement une sous-classe de DeviceMediator dans votre test afin de pouvoir retourner l'objet CallMonitor.

Déplacez l'argument constructeur pour CallMonitor dans une méthode/propriété appelée dans le constructeur DeviceMediator.

Il semble que vous essayez d'écouter un événement quelconque sur CallMonitor, donc vous pouvez (et devriez le cas échéant) ajouter un événement auquel le DeviceMediator est abonné.Dans ce cas, vous pouvez utiliser RhinoMock pour se moquer de l'événement de collecte appel comme ceci:

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

Cependant, comme il est indiqué ci-dessus, vous ne pouvez pas les appels constructeur maquette en utilisant RhinoMock car cela nécessiterait des changements à l'IL (en supposant généré il est même possible).

+0

Comme je l'ai dit dans ma question, je dois passer pas ICallMonitor dans le cadre du constructeur. (J'ai déjà fait cela avec mes autres interfaces qui peuvent être passées.) – Vaccano