2017-07-27 1 views
1

In C# wirft Moq VerifySet Expression is not a property setter invocation., wenn der Setter nicht trivial ist, auch wenn SetupProperty oder SetupSet verwenden.C# Moq VerifySet-Übergaben Ausdruck ist kein Eigenschaften-Setter-Aufruf für nicht-triviale Setter

Hier ist ein triviales Beispiel. Beachten Sie, dass die Antlers Setter trivial ist und die Antlers2 Setter ist nicht trivial .:

public class Dancer 
{ 

    public Dancer(bool pIsMale) 
    { 
     IsMale = pIsMale; 
    } 

    private bool _IsMale; 
    public virtual bool IsMale { get { return this._IsMale; } private set { this._IsMale = value; } } 

    private bool _Antlers; 
    public virtual bool Antlers 
    { 
     get { return this._Antlers; } 
     set 
     { 
      this._Antlers = value; 
     } 
    } 

    public virtual bool Antlers2 
    { 
     get { return this._Antlers; } 
     set 
     { 
      // females cannot have antlers 
      if (IsMale) 
       this._Antlers = value; 
      else 
       this._Antlers = false; 
     } 
    } 
} 

Hier sind die Unit-Tests. Der zweite Satz von drei (mit Antlers2) ist ansonsten identisch mit dem ersten Satz von drei (mit Antlers). Alle Unit Tests mit Antlers bestehen den Test. Alle Komponententests, die Antlers2 verwenden, werfen Expression is not a property setter invocation., selbst wenn SetupProperty wo ich dachte, die gesamte Implementierung der Eigenschaft wird vollständig ignoriert und durch Moq ersetzt.

public class DancerTests 
{ 
    [Fact] 
    public void Antlers_NoSetup() 
    { 
     // Arrange 

     // create mock of class under test 
     var sut = new Mock<Dancer>(true) { CallBase = true }; 

     // Act 
     sut.Object.Antlers = true; 

     // Assert 
     sut.VerifySet(x => x.Antlers = true); 
    } 

    [Fact] 
    public void Antlers_SetupProperty() 
    { 
     // Arrange 

     // create mock of class under test 
     var sut = new Mock<Dancer>(true) { CallBase = true }; 
     sut.SetupProperty(x => x.Antlers, false); 

     // Act 
     sut.Object.Antlers = true; 

     // Assert 
     sut.VerifySet(x => x.Antlers = true); 
    } 

    [Fact] 
    public void Antlers_SetupSet() 
    { 
     // Arrange 

     // create mock of class under test 
     var sut = new Mock<Dancer>(true) { CallBase = true }; 
     sut.SetupSet(x => x.Antlers = true); 

     // Act 
     sut.Object.Antlers = true; 

     // Assert 
     sut.VerifySet(x => x.Antlers = true); 
    } 

    [Fact] 
    public void Antlers2_NoSetup() 
    { 
     // Arrange 

     // create mock of class under test 
     var sut = new Mock<Dancer>(true) { CallBase = true }; 

     // Act 
     sut.Object.Antlers2 = true; 

     // Assert 
     sut.VerifySet(x => x.Antlers2 = true); 
    } 

    [Fact] 
    public void Antlers2_SetupProperty() 
    { 
     // Arrange 

     // create mock of class under test 
     var sut = new Mock<Dancer>(true) { CallBase = true }; 
     sut.SetupProperty(x => x.Antlers2, false); 

     // Act 
     sut.Object.Antlers2 = true; 

     // Assert 
     sut.VerifySet(x => x.Antlers2 = true); 
    } 

    [Fact] 
    public void Antlers2_SetupSet() 
    { 
     // Arrange 

     // create mock of class under test 
     var sut = new Mock<Dancer>(true) { CallBase = true }; 
     sut.SetupSet(x => x.Antlers2 = true); 

     // Act 
     sut.Object.Antlers2 = true; 

     // Assert 
     sut.VerifySet(x => x.Antlers2 = true); 
    } 

} 

Was ist es mit nicht-trivialen Basisklasseneigenschaften, die Moqs VerifySet verwechseln? Ich verwende Moq 4.7.99, Visual Studio 2015, Targeting .Net Framework 4.5.2.

Danke für die Hilfe! Ich habe stark von StackOverflow profitiert!

Antwort

0

Es sieht also so aus, als hätten Sie eine Konfluenz von Bedingungen, die dieses seltsame Szenario erzeugen. Es kann ein Fehler im Wert reporting sein, ich bin mir nicht sicher. Wie auch immer, der Haken in Ihrem Schwindel scheint von der Einstellung Callbase = true und/oder Anruf IsMale aus Ihrem Antler2 Property Setter kommen.

Einstellung Callbase = true erstellt eine MockInvocation für jede virtuelle Methode in Ihrer Klasse - einschließlich IsMale (das wird später wichtig). Auf den ersten Blick scheint das keine große Sache zu sein, aber es stellt sich heraus, dass es so ist. Die Ausnahme wird in Moq in der SetupSetImpl-Methode geworfen. Diese Methode überprüft, dass der letzte MockInvocation, der von Moq aufgerufen wurde, ein Setter war - es überprüft das, dass der Name der letzten Aufruf-Methode mit "Set_" beginnt.

Mit einem normalen 'Trivial Setter' wäre der letzte Aufruf der Setter für Antlers2, aber Sie haben eine Wendung eingeführt. In Ihrem Setter rufen Sie IsMale und da Sie auch Callbase = true setzen, als Sie Ihren Mock erstellt haben, gibt es eine MockInvocation für Ihre IsMale Eigenschaft. Was also passiert, wenn Sie die Eigenschaft Antler2 setzen, ist, dass 'set_Antler2' zur Aufrufliste hinzugefügt wird und dann, wenn IsMale aufgerufen wird, der Antler2 Eigenschaften-Setter 'get_IsMale' zur Aufrufliste hinzugefügt wird. Wenn also SetupSetImpl prüft, ob der letzte Aufruf ein Setter war, findet er stattdessen einen Getter und Panics.

Ich habe getestet und bestätigt, dass es mindestens zwei Möglichkeiten gibt, diese Ausnahme zu verhindern. Dies ist zuerst Callbase = true nicht zu setzen. Dies ist jedoch ein Problem, da Sie das Scheinobjekt benötigen, um den Antler2-Eigenschaft-Setter an das Basisobjekt zu übergeben - das ist der gesamte Punkt Ihres Tests. Die andere, einfachere Lösung ist, die IsMale Eigenschaft ganz zu umgehen und einfach das dahinter liegende Feld von der Antler2 Eigenschaft Setter wie diese direkt anrufen:

if (this._IsMale == true) // Notice I'm calling _IsMale 
    this._Antlers2 = value; 
else 
    this._Antlers2 = false; 

Dies auch mit Callbase = true Satz funktioniert, weil Sie nicht die IsMale Eigenschaft aufrufen, und daher get_IsMale nicht zur Aufrufliste hinzufügen. Stattdessen überprüfen Sie einfach das Sicherungsfeld, das die IsMale-Eigenschaft verwendet.

+0

Danke. Tolle Erklärung! Ich habe dies tatsächlich als Bug zu GitHub/Moq gepostet. Siehe [hier] (https://github.com/moq/moq4/issues/430). –

+0

Etwas Neues ist mir aufgefallen. Die Ausführung von VerifySet verursacht die Ausführung des Eigenschaftensetzers. Selbst wenn die Eigenschaft mit einer SetupProperty verspottet wurde, führt das VerifySet die Implementierung des aktuellen Property Setters erneut aus. ** Warum führt VerifySet den Property Setter überhaupt aus? ** Soll nicht einfach gemeldet werden, ob der Property Setter bereits von der Testausführung ausgeführt wurde? Ich glaube, dass die erneute Ausführung des tatsächlichen Eigentums-Setters die Ursache des Fehlers ist, obwohl ich immer noch glaube, dass Sie bezüglich der "letzten Aufruf-Methode" oben genau an einem anderen Ort korrekt sind. –