2009-04-07 10 views
18

Ich bin gerade auf Moq umgestiegen und habe ein Problem festgestellt. Ich teste eine Methode, die eine neue Instanz eines Geschäftsobjekts erstellt, setzt die Eigenschaften des Objekts auf Benutzereingabewerte und ruft eine Methode (SaveCustomerContact) auf, um das neue Objekt zu speichern. Das Geschäftsobjekt wird als Ref-Argument übergeben, da es eine Remoting-Schicht durchläuft. Ich muss testen, dass das Objekt, das an SaveCustomerContact übergeben wird, alle seine Eigenschaften wie erwartet festgelegt hat, aber da es in der Controller-Methode als neu instanziiert wird, kann ich nicht so scheinen.Wert des Referenzparameters mit Moq überprüfen

public void AddContact() { 

    var contact = new CustomerContact() { CustomerId = m_model.CustomerId }; 

    contact.Name = m_model.CustomerContactName; 
    contact.PhoneNumber = m_model.PhoneNumber; 
    contact.FaxNumber = m_model.FaxNumber; 
    contact.Email = m_model.Email; 
    contact.ReceiveInvoiceFlag = m_model.ReceiveInvoiceFlag; 
    contact.ReceiveStatementFlag = m_model.ReceiveStatementFlag; 
    contact.ReceiveContractFlag = m_model.ReceiveContractFlag; 
    contact.EmailFlag = m_model.EmailFlag; 
    contact.FaxFlag = m_model.FaxFlag; 
    contact.PostalMailFlag = m_model.PostalMailFlag; 
    contact.CustomerLocationId = m_model.CustomerLocationId; 

    RemotingHandler.SaveCustomerContact(ref contact); 
} 

Hier ist der Test:

[TestMethod()] 
public void AddContactTest() { 

    int customerId = 0; 

    string name = "a"; 

    var actual = new CustomerContact(); 

    var expected = new CustomerContact() { 
     CustomerId = customerId, 
     Name = name 
    }; 

    model.Setup(m => m.CustomerId).Returns(customerId); 
    model.SetupProperty(m => model.CustomerContactName, name); 
    model.SetupProperty(m => m.PhoneNumber, string.Empty); 
    model.SetupProperty(m => m.FaxNumber, string.Empty); 
    model.SetupProperty(m => m.Email, string.Empty); 
    model.SetupProperty(m => m.ReceiveInvoiceFlag, false); 
    model.SetupProperty(m => m.ReceiveStatementFlag, false); 
    model.SetupProperty(m => m.ReceiveContractFlag, false); 
    model.SetupProperty(m => m.EmailFlag, false); 
    model.SetupProperty(m => m.FaxFlag, false); 
    model.SetupProperty(m => m.PostalMailFlag, false); 
    model.SetupProperty(m => m.CustomerLocationId, 0); 

    remote 
     .Setup(r => r.SaveCustomerContact(ref actual)) 
     .Callback(() => Assert.AreEqual(actual, expected)); 

    target.AddContact(); 

} 

Dies ist nur die jüngste von vielen Versuchen ahold diesen Parameter zu erhalten. Als Referenz ändert sich der Wert von Ist nicht von seinem anfänglichen (konstruierten) Zustand.

Verschieben der Assert.AreEqual (erwartet, aktuell), nachdem der Zielaufruf fehlgeschlagen ist. Wenn ich .Verifizierbar() zum Setup anstelle von .CallBack hinzufügen und dann remote.Verify nach dem Ziel aufrufen (oder, vermute ich, setze den Mock auf strict), schlägt es immer fehl, weil der Parameter, den ich im Test zur Verfügung stelle, nicht der ist dieselbe Instanz wie diejenige, die in der Controller-Methode erstellt wurde.

Ich verwende Moq 3.0.308.2. Irgendwelche Ideen, wie man das prüft, würden geschätzt werden. Vielen Dank!

Antwort

19

Ich kann Ihnen keine genaue Lösung anbieten, aber eine Alternative wäre, die Pass-by-Ref-Semantik hinter einem Adapter zu verstecken, der den Parameter nach Wert übernimmt und an den RemotingHandler weiterleitet.Dies wäre einfacher zu verspotten, und würde die „ref“ Warze von der Schnittstelle (Ich bin immer suspekt ref Parameter :-))

EDIT entfernen:

Oder Sie einen Stub anstelle eines Mock verwenden könnte zum Beispiel:

public class StubRemotingHandler : IRemotingHandler 
{ 
    public CustomerContact savedContact; 

    public void SaveCustomerContact(ref CustomerContact contact) 
    { 
     savedContact = contact; 
    } 
} 

Sie können nun das gespeicherte Objekt in Ihrem Test prüfen:

IRemotingHandler remote = new StubRemotingHandler(); 
... 
//pass the stub to your object-under-test 
... 
target.AddContact(); 
Assert.AreEqual(expected, remote.savedContact); 

Sie auch in Ihrem Kommentar sagen:

Ich hasse würde einen Präzedenzfall von Einwickeln Zufallsbits des Back-End zu starten, so kann ich Tests schreiben leichter

Ich denke, das ist genau der Präzedenzfall Sie einstellen müssen! Wenn Ihr Code nicht testbar ist, werden Sie sich weiter darum bemühen, ihn zu testen. Erleichtern Sie es zu testen und erhöhen Sie Ihre Abdeckung.

+0

Ich denke, ich bin mir nicht sicher, wie du das auch stubst (obwohl ich nicht viel über Stubs weiß). Können Sie näher erläutern, wie damit umgegangen werden könnte? –

+0

Auch so, wie ich Ihnen in der Ref-Frage zustimme, funktioniert unsere Remoting-Schicht so, und ich würde es hassen, einen Präzedenzfall für das Einfügen zufälliger Bits des Backends zu erstellen, damit ich Tests einfacher schreiben kann. –

+0

Ich hatte ein ähnliches Problem und habe mich so darauf eingelassen, dass MoQ funktioniert, ich habe vergessen, dass ich meinen eigenen Stub schreiben könnte! Vielen Dank. –

9

Leider bin ich mir nicht sicher, dass dies ohne direkte Unterstützung von Moq möglich ist. Das Problem ist, dass Lambda-Ausdrücke ref oder out nicht unterstützen.

„Ein Lambda-Ausdruck einen ref oder out-Parameter von einer umschließenden Methode nicht direkt erfassen kann.“

http://msdn.microsoft.com/en-us/library/bb397687.aspx

ich nicht einmal ein Beispiel bekommen wie das Ihre arbeiten. Das Hinzufügen von ref zum Setup kann nicht kompiliert werden.

Sie könnten die Moq Diskussionen für mehr http://groups.google.com/group/moqdisc

Viel Glück zu sehen.

+0

Große Antwort! :) – Delashmate

10

Die neueste Version von Moq unterstützt dieses Szenario.

vom quickstart Genommen am http://code.google.com/p/moq/wiki/QuickStart:

// ref arguments 
var instance = new Bar(); 
// Only matches if the ref argument to the invocation is the same instance 
mock.Setup(foo => foo.Submit(ref instance)).Returns(true); 
+6

Danke für die Antwort. Ich habe das im Schnellstart gesehen (in der Tat habe ich meinen Chef davon überzeugt, auf die neueste Version von Moq zu aktualisieren, weil ich darauf vertraut habe, dass es funktioniert). Leider scheint das Verhalten nicht dem zu folgen, was (sehr unklar) von "Nur Übereinstimmungen, wenn das ref-Argument für den Aufruf dieselbe Instanz ist" vorgeschlagen wird. –

0

ich ähnliches Problem begegnet sind. Bit Ich habe die Lösung mit dem letzten Moq und übergibt den Wert wie

var instance = new Bar(); Mock.Setup (foo => foo.Submit (ref instance)). Gibt (true) zurück;

Früher auch, ich habe die gleiche Methode verwendet, aber ich wurde nicht so wahr zurückkommen.

Innerhalb der tatsächlichen Funktionsinstanz wurde erstellt und das Überschreiben der von der Unit-Testklasse übergebenen Instanz verursachte das Problem. Ich habe die Instanzerstellung in der eigentlichen Klasse entfernt und dann funktioniert es.

Ich hoffe, es wird Ihnen helfen.

danke