2017-11-20 1 views
2

Ich bin neu zu testen und schreiben testbaren Code, und bin auf der Suche nach einigen Erklärungen über die richtige Art und Weise mit diesem einfachen Szenario umzugehen. Ich habe andere Fragen und Antworten zu SO mit ähnlichen Titeln gelesen, aber sie scheinen keine klare Antwort auf meine Frage zu geben.Wie Unit-Test-Modell-Methode, die Methode auf einem anderen Modell in Laravel ruft

Ich habe einen Controller, der die shipped() Methode auf einer Instanz von meiner Picking Klasse ruft:

class MyController extends \BaseController { 

    public function controllerMethod() { 
     $picking = new Picking; 
     $picking->shipped($shipmentData); 
    } 
} 

Das Picking Modell sieht wie folgt aus:

class Picking extends \Eloquent { 

    public function order() { 
     return $this->belongsTo('Order'); 
    }  

    public function shipped($shipmentData) { 
     $this->carrier = $shipmentData['Carrier']; 
     $this->service = $shipmentData['Service']; 
     $this->is_shipped = true; 
     $this->save(); 

     $this->order->pickingShipped(); 
    } 
} 

Wie Sie sehen können, diese shipped() Methode speichert einige Daten und ruft dann die pickingShipped()-Methode auf, es ist Order verwandt.

Jetzt versuche ich, einen Test für die shipped() Methode zu schreiben, und ich bin mir nicht sicher, der geeignete Weg, dies zu tun. Ich habe über Spott gelesen, aber ich bin verwirrt, wenn dies eine Situation ist, in der Spott notwendig ist. Ich habe an einige mögliche Lösungen gedacht, aber ich bin mir nicht sicher, ob einige von ihnen richtig sind.

1) Ordnen Sie den Code so an, dass der Controller die pickingShipped()-Methode aufruft, wodurch er aus der shipped()-Methode entfernt werden kann, wodurch der Test vereinfacht wird.

Zum Beispiel würde die letzte Zeile des shipped() Verfahrens entfernt werden, und der Controller-Code würde sich ändern:

$picking = new Picking; 
$picking->shipped($shipmentData); 
$picking->order->pickingShipped(); 

2) Bei dem Test auf order ein mock Verfahren so, dass die Verwendung Test kann einfach bestätigen, dass die pickingShipped() Methode aufgerufen wird.

Etwas in der Richtung was erklärt wird here. Das würde bedeuten, den Test so etwas tun könnte:

$order->expects($this->once())->method('pickingShipped') 

Aber ich denke, das würde bedeuten, dass ich auch eher die Reihenfolge Abhängigkeit zu injizieren als unter Berufung auf die order Beziehung innerhalb des shipped() Methode, wie folgt aus:

class Picking extends \Eloquent { 

    public function order() { 
     return $this->belongsTo('Order'); 
    }  

    public function shipped(Order $order, $shipmentData) { 
     $this->carrier = $shipmentData['Carrier']; 
     $this->service = $shipmentData['Service']; 
     $this->is_shipped = true; 
     $this->save(); 

     $order->pickingShipped(); 
    } 
} 

Und dann der Code in der Steuerung würde wie folgt aussehen haben:

$picking = new Picking; 
$picking->shipped($picking->order, $shipmentData); 

das fühlt sich ein wenig stran ge, aber ich bin mir wirklich nicht sicher, was richtig ist.

Meine Frage ist, was ist der richtige Weg, um diesen Code zu schreiben und zu testen? Es ist einfach zu testen, die shipped() Methode setzt die entsprechenden Daten auf sich selbst, aber was ist mit diesem Anruf an pickingShipped() am Ende? Dies scheint die Tests komplizierter zu machen. Soll der Code also neu geordnet werden? Wenn das so ist, wie? Oder ist das ein häufiger Anwendungsfall für Spott, wie ich es in der 2. Option umschrieben habe? Wenn ja, ist es richtig, die Abhängigkeit zu injizieren, wie ich es zeige?

Antwort

0

Ich bin kein PHP-Entwickler, so könnte dies auf Sprachfunktionen als Blocker kommen.

Ich würde vorschlagen, dass die Abhängigkeitsinjektionsmethode besser ist, da sie die Abhängigkeit ausgibt und es Ihnen ermöglicht, Ihre Persistenz und Ihr Verhalten später zu trennen. Zum Beispiel könnte die Picking oder Picker ein besseres Benehmen sein, während PickingRecord für die Daten nett sein könnte.

Auf jeden Fall, wenn Sie Standardargumente in PHP festlegen können dann mag ich die letzte Methode, die Sie verwendet (Injektion) und Sie gerade so etwas wie

public function shipped($shipmentData, Order $order = $this->order) { 
    $this->carrier = $shipmentData['Carrier']; 
    $this->service = $shipmentData['Service']; 
    $this->is_shipped = true; 
    $this->save(); 

    $order->pickingShipped(); 
} 

Sie die order Diese dann würde es vereinfachen könnte ignorieren Abhängigkeit in Produktionscode und injizieren Sie eine doppelte oder andere Art von Objekt als order in Tests und einfach behaupten, dass die Methode auf dem Objekt order aufgerufen wurde. Integrationstests sollten weiterhin überwachen, dass die Schnittstellen immer noch ineinandergreifen, obwohl Sie in Ihren Komponententests Doubles injizieren.

So würde ich versuchen, dies in Ruby zu tun.

+0

Danke für die Rückmeldung! Mein einziges Problem bei diesem Ansatz besteht darin, dass wir zulassen, dass eine '$ order' in die Methode übernommen wird, obwohl dies in der Realität nicht erlaubt sein sollte. Der Sinn der Methode ist, dass sie 'PickingShipped' auf der Kommissionierreihenfolge aufrufen muss, nicht auf irgendeiner Bestellung. Das bedeutet, dass wir diesen Parameter nur zu Testzwecken hinzufügen. Es ist seltsam, die Methodensignatur nur zu Testzwecken zu ändern. Jetzt schlägt die Methode vor, dass es für ein Verhalten verwendet werden sollte, das es eigentlich nicht sollte. – flyingL123

+0

In Bezug auf Standardargumente können Sie mit yes PHP übergeben, aber sie können keinen dynamischen Wert wie '$ this-> order' haben. In diesem Fall würde ich den Standardwert auf "null" setzen und dann am Anfang der Methode sagen: if (is_null ($ order)) $ order = $ this-> order; ', was dasselbe bewirkt, aber wie ich oben sagte, fühlt sich diese Abhängigkeitsinjektion immer noch nicht richtig an. – flyingL123

0

Ich habe eine Lösung gefunden, die mir gut tut. Es scheint ziemlich offensichtlich, jetzt, wo ich es sehe. Alles, was ich tat, war die Eigenschaft $picking->order, die verspottete Reihenfolge für den Test zurückzugeben.

$order = Mockery::mock(Order::class); 

$picking = new Picking; 
$picking->order = $order; 

$order->shouldReceive('pickingShipped') 
    ->with($picking) 
    ->once(); 

$picking->shipped($shipmentData); 

Wenn nun die shipped() Methode $this->order ruft, wird es das verspottet $order Objekt I definiert sind, und der Test ordnungsgemäß funktioniert.

Das fühlt sich wie die richtige Lösung an.

Verwandte Themen