2009-05-24 4 views
1

Ich schreibe einige Testfälle, und ich habe einen Testfall, der Mock-Objekte verwendet. Ich muss überprüfen, ob zwei Klassenmethoden von einer anderen Klassenmethode aufgerufen werden. Hier ist, was ich getan habe:SimpleTest Mocking Problem

Zuerst habe ich die Mock generiert:

Mock::generate('Parser'); 

Dann in meinem Test habe ich genannt:

$P = new MockParser(); 

$P->expectOnce('loadUrl', array('http://url')); 
$P->expectOnce('parse'); 

$P->fetchAndParse('http://url'); 

Meine Implementierung Code wie folgt aussieht:

public function fetchAndParse($url) { 
    $this->loadUrl($url); 
    $this->parse(); 
} 

Und die loadUrl und parse() Methoden sind definitiv vorhanden. Ich erhalte bei meinen Tests zwei Fehler, die mir beide sagen: "Die erwartete Anrufzahl für [loadUrl] war [1] hat [0]". Ich habe keine Ahnung, was passiert - die Methoden werden von dieser Funktion aufgerufen!

Danke,

Jamie

Antwort

4

Während meine Erfahrung mit spöttischer Frameworks in der .NET-Welt gewesen ist, glaube ich, dass das, was Sie versuchen, falsch zu tun ist.

Jedes Spott Framework, wenn gefragt, einen Mock für eine Klasse zu erstellen, generiert "Stubs" für alle Methoden in dieser Klasse. Dies beinhaltet die Methode fetchAndParse. Wenn Sie also fetchAndParse für Ihr Mock-Objekt $ P aufrufen, werden die Methoden loadUrl und parse NICHT aufgerufen. Was Sie wirklich tun, ist die "stubbed" Methode fetchAndParse aufzurufen.

Ich bin nicht wirklich erfahren in PHP, also möchte ich nicht versuchen, Ihren Test zu reparieren. Hoffentlich kann jemand anderes das tun.

+0

Das ist wirklich hilfreich - es bedeutet, dass ich ziemlich viel Code ändern muss, aber zumindest weiß ich jetzt, was vor sich geht! Vielen Dank! –

1

Sie haben missverstanden, wie Spott funktioniert. Wenn Sie die Abhängigkeitsinjektion verwenden, um ein Hilfsobjekt in Ihrer Klasse zu definieren, können Sie Ihr injiziertes Objekt vortäuschen. Danach können Sie das Verhalten (Interface) des ursprünglichen Objekts simulieren. Der bessere Weg besteht darin, Interfaces nachzuahmen, da Sie entwickeln können, ohne eine Klasse zu erstellen, die die aktuelle Schnittstelle implementiert.

Durch Ihr Beispiel:

interface UrlLoaderInterface { 

    public function load($url); 
} 

class YourParser { 

    protected $urlLoader; 
    protected $source; 

    public function setUrlLoader(UrlLoaderInterface $urlLoader) { 
     $this->urlLoader = $urlLoader; 
    } 

    public function fetchAndParse($url) { 
     $this->loadUrl($url); 
     $this->parse(); 
    } 

    public function loadUrl($url) { 
     $this->source = $this->urlLoader->load($url); 
    } 

    public function parse() { 

    } 

} 

Mock::generate('UrlLoaderInterface', 'MockUrlLoader'); 

class TestYourParser extends UnitTestCase { 

    public function testShouldCallUrlLoaderByFetchAndParse() { 
     $testUrl = 'http://url'; 

     $urlLoader = new MockUrlLoader(); 
     $urlLoader->expectOnce('load', array($testUrl)); 
     $urlLoader->returns('load', 'source', array($testUrl)); 

     $parser = new YourParser(); 
     $parser->setUrlLoader($urlLoader); 
     $parser->fetchAndParse($testUrl); 
    } 

} 

Btw. Ihr Beispiel ist ein Fehler, weil Methodennamen keine Wörter wie 'und', 'oder', 'if' usw. enthalten dürfen. Eine Methode darf nur eine Sache machen. Wenn Sie diese Wörter verwenden, können Sie sicher sein, dass Sie einen schlecht konzipierten Code haben.