2009-08-18 12 views
18

Ich modifiziere eine Klassenmethode, die einige Eingabeparameterdaten formatiert, die anschließend als Parameter in einem Methodenaufruf in die Basisklasse (die in einer anderen Assembly lebt) verwendet werden.Verspotten eines Basisklassenmethodenaufrufs mit Moq

Ich möchte überprüfen, ob die Daten, die ich an meine Methode übergebe, im richtigen Format vorliegen, wenn sie an die Basisklassenmethode übergeben werden, also möchte ich den Basisklassenmethodenaufruf Moq. Ist das mit Moq möglich?

Antwort

12

Wenn ich Ihre Frage richtig verstanden habe, haben Sie eine Klasse A in einem anderen Assembly definiert, und dann eine Klasse B implementiert mehr oder weniger wie folgt aus:

public class B : A 
{ 
    public override MyMethod(object input) 
    { 
     // Do something 
     base.MyMethod(input); 
    } 
} 

Und jetzt wollen Sie, dass die Basis zu überprüfen. MyMethod heißt?

Ich sehe nicht, wie Sie dies mit einer dynamischen Scheinbibliothek tun können. Alle dynamischen Mock-Bibliotheken (mit Ausnahme von TypeMock) arbeiten, indem sie dynamisch Klassen ausgeben, die von dem fraglichen Typ abstammen.

In Ihrem Fall können Sie nicht sehr gut Moq fragen von A, abzuleiten, da Sie B testen möchten.

Das bedeutet, dass Sie Moq bitten müssen, Ihnen eine Mock<B> zu geben. Dies bedeutet jedoch, dass der emittierte Typ von B abgeleitet ist, und obwohl MyMethod (das immer noch virtuell ist) überschrieben und seine Basis (B.MyMethod) aufgerufen werden kann, hat er keine Möglichkeit, zur ursprünglichen Klasse zu gelangen und zu überprüfen, ob B base.MyMethod aufruft .

Stellen Sie sich vor, dass Sie eine Klasse schreiben (C), die von B. leitet Während Sie MyMethod außer Kraft setzen können, gibt es keine Möglichkeit, Sie überprüfen können, dass B ruft A:

public class C : B 
{ 
    public override MyMethod(object input) 
    { 
     // How to verify that base calls its base? 
     // base in this context means B, not A 
    } 
} 

Wieder mit der möglichen Ausnahme von TypeMock, Dynamic Mock-Bibliotheken können nichts tun, was Sie nicht manuell tun können.

Allerdings würde ich davon ausgehen, dass das Aufrufen der Basismethode, die Sie zu überprüfen versuchen, eine beobachtbare Nebenwirkung hat. Wenn möglich, können Sie also anstelle des verhaltensbasierten Tests zustandsbasierte Tests verwenden, um das Ergebnis des Aufrufs der Methode zu überprüfen ?

In jedem Fall sollte das statusbasierte Testen in den meisten Fällen Ihr Standardansatz sein.

1

Stimmen Sie mit Mark überein, es ist nicht möglich, Moq zu verwenden.

Abhängig von Ihrer Situation können Sie swithcing from inheritance to composition betrachten. Dann können Sie sich über die Abhängigkeit lustig machen und Ihre Methode überprüfen. Natürlich lohnt es sich in manchen Fällen nicht.

1

Umbrechen Sie die Basisklassenmethode in eine Methode und richten Sie diese Methode z.

public class B : A 
{ 
    public virtual BaseMyMethod(object input) 
    { 
     // Do something 
     base.MyMethod(input); 
    }  
public override MyMethod(object input) 
    { 
     // Do something 
     BaseMyMethod(input); 
    } 
} 

und jetzt Einrichten der BaseMyMethod

0

fand ich diese Lösung - hässlich, aber es könnte funktionieren.

var real = new SubCoreClass();       
var mock = new Mock<SubCoreClass>(); 
mock.CallBase = true; 

var obj = mock.Object; 

mock 
    .Setup(c => c.Execute()) 
    .Callback(() => 
     {                  
     obj.CallBaseMember(typeof(Action), real, "Execute");    
     Console.WriteLine(obj.GetHashCode()); 
     } 
    ); 

public static Delegate CreateBaseCallDelegate(object injectedInstance, Type templateDelegate, object instanceOfBase, string methodName) 
{ 
    var deleg = Delegate.CreateDelegate(templateDelegate, instanceOfBase, methodName); 
    deleg.GetType().BaseType.BaseType.GetField("_target", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(deleg, injectedInstance); 

    return deleg; 
} 

public static object CallBaseMember(this object injectedInstance, Type templateDelegate, object instanceOfBase, string methodName, params object[] arguments) 
{ 
    return CreateBaseCallDelegate(injectedInstance, templateDelegate, instanceOfBase, methodName).DynamicInvoke(arguments); 
} 
3

Es ist durchaus möglich, Basisklasse spottend. Aber Sie müssen die Zielklasse ändern.

Für z.DerivedClass erweitert BaseClass. Baseclass hat Methoden MethodA(), MethodB(), MethodC() ... Die DerivedClass hat diese Methode:

void MyMethod() { 
    this.MethodA(); 
    this.MethodB(); 
    this.MethodC(); 
} 

Sie auf Mock Basisklasse wollen, um zu validieren dass alle MethodA(), MethodB(), MethodC() werden innerhalb MyMethod() genannt.

Sie haben ein Feld in dem DerivedClass zu erstellen:

class DerivedClass { 
    private BaseClass self = this; 
    ... 
} 

Und Sie haben auch die MyMethod ändern():

void MyMethod() { 
    self.MethodA(); 
    self.MethodB(); 
    self.MethodC(); 
} 

auch ein Verfahren hinzufügen, der injizieren Sie die this.self Feld mit Mock-Objekt

public void setMock(BaseClass mock) { 
    this.self = mock; 
} 

Jetzt können Sie verspotten:

DerivedClass target = new DerivedClass(); 
BaseClass mock = new Mock(typeof(BaseClass)); 
target.setMock(mock); 
target.MyMethod(); 

mock.verify(MethodA); 
mock.verify(MethodB); 
mock.verify(MethodC); 

diese Technik verwenden, können Sie auch verschachtelte Methodenaufrufe Mock.

+0

Compilerfehler: Schlüsselwort 'this' ist im aktuellen Kontext nicht verfügbar. Im Feld Abgeleitete Klasse. –

7

Ab 2013 mit neuesten Moq können Sie. Hier ist ein example

public class ViewModelBase 
{ 
    public virtual bool IsValid(DateTime date) 
    { 
     //some complex shared stuff here 
    } 
} 

public class MyViewModel : ViewModelBase 
{ 
    public void Save(DateTime date) 
    { 
     if (IsValid(date)) 
     { 
      //do something here 
     } 
    } 
} 

public void MyTest() 
{ 
    //arrange 
    var mockMyViewModel = new Mock<MyViewModel>(){CallBase = true}; 
    mockMyViewModel.Setup(x => x.IsValid(It.IsAny<DateTime>())).Returns(true); 

    //act 
    mockMyViewModel.Object.Save(); 

    //assert 
    //do your assertions here 
} 
Verwandte Themen